Pārlūkot izejas kodu

合并开源主线

648540858 2 gadi atpakaļ
vecāks
revīzija
b4048fbe80
100 mainītis faili ar 1870 papildinājumiem un 645 dzēšanām
  1. 3 2
      README.md
  2. 1 1
      doc/_content/ability/gis.md
  3. 21 2
      doc/_content/introduction/deployment.md
  4. 14 11
      pom.xml
  5. 12 0
      sql/2.6.6-2.6.7更新.sql
  6. 3 0
      src/main/resources/db/migration/V2.6.7_20230201__初始化.sql
  7. 0 2
      src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
  8. 8 4
      src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
  9. 1 3
      src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java
  10. 23 0
      src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java
  11. 0 3
      src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java
  12. 15 7
      src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
  13. 12 1
      src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
  14. 6 3
      src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java
  15. 39 0
      src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
  16. 0 64
      src/main/java/com/genersoft/iot/vmp/conf/druid/DruidConfiguration.java
  17. 0 24
      src/main/java/com/genersoft/iot/vmp/conf/druid/EnableDruidSupport.java
  18. 9 11
      src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java
  19. 9 6
      src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java
  20. 0 24
      src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java
  21. 84 0
      src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java
  22. 138 0
      src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java
  23. 11 2
      src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java
  24. 13 3
      src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java
  25. 56 66
      src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
  26. 53 0
      src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java
  27. 9 0
      src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java
  28. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java
  29. 23 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
  30. 11 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java
  31. 10 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java
  32. 11 2
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
  33. 19 8
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java
  34. 1 4
      src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java
  35. 2 2
      src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java
  36. 1 2
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
  37. 4 7
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
  38. 14 9
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
  39. 30 19
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
  40. 48 16
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
  41. 2 2
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
  42. 33 21
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
  43. 14 6
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
  44. 50 6
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
  45. 7 7
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
  46. 32 29
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
  47. 13 5
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
  48. 2 2
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
  49. 3 2
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java
  50. 12 5
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java
  51. 2 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
  52. 2 5
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java
  53. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java
  54. 7 2
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
  55. 8 6
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java
  56. 5 5
      src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java
  57. 27 2
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
  58. 2 2
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
  59. 4 1
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
  60. 400 3
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java
  61. 9 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
  62. 3 2
      src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java
  63. 8 1
      src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java
  64. 12 4
      src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
  65. 12 2
      src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
  66. 29 0
      src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
  67. 52 12
      src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java
  68. 97 18
      src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
  69. 73 16
      src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
  70. 1 1
      src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
  71. 45 9
      src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java
  72. 2 1
      src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java
  73. 2 1
      src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java
  74. 2 1
      src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java
  75. 2 1
      src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusListMsgListener.java
  76. 2 1
      src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java
  77. 2 1
      src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisStreamMsgListener.java
  78. 8 1
      src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
  79. 53 25
      src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
  80. 21 7
      src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
  81. 7 3
      src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java
  82. 9 6
      src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
  83. 20 4
      src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
  84. 0 1
      src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java
  85. 7 1
      src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java
  86. 4 0
      src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java
  87. 1 1
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java
  88. 1 1
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java
  89. 11 17
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java
  90. 0 4
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java
  91. 1 1
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java
  92. 5 2
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
  93. 1 1
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java
  94. 1 1
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java
  95. 9 54
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java
  96. 1 1
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
  97. 1 1
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
  98. 7 9
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java
  99. 8 11
      src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java
  100. 0 0
      src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java

+ 3 - 2
README.md

@@ -99,15 +99,16 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
 - [X] 支持接口鉴权
 - [X] 支持接口鉴权
 - [X] 云端录像,推流/代理/国标视频均可以录制在云端服务器,支持预览和下载
 - [X] 云端录像,推流/代理/国标视频均可以录制在云端服务器,支持预览和下载
 - [X] 支持打包可执行jar和war
 - [X] 支持打包可执行jar和war
+- [X] 支持跨域请求,支持前后端分离部署
  
  
 
 
 # 遇到问题如何解决
 # 遇到问题如何解决
 国标最麻烦的地方在于设备的兼容性,所以需要大量的设备来测试,目前作者手里的设备有限,再加上作者水平有限,所以遇到问题在所难免;
 国标最麻烦的地方在于设备的兼容性,所以需要大量的设备来测试,目前作者手里的设备有限,再加上作者水平有限,所以遇到问题在所难免;
-1. 查看wiki,仔细的阅读可以帮你避免几乎所有的问题
+1. 查看文档网站,仔细的阅读可以帮你避免几乎所有的问题
 2. 搜索issues,这里有大部分的答案
 2. 搜索issues,这里有大部分的答案
 3. 加QQ群(901799015),这里有大量热心的小伙伴,但是前提新希望你已经仔细阅读了wiki和搜索了issues。
 3. 加QQ群(901799015),这里有大量热心的小伙伴,但是前提新希望你已经仔细阅读了wiki和搜索了issues。
 4. 你可以请作者为你解答,但是我不是免费的。
 4. 你可以请作者为你解答,但是我不是免费的。
-5. 你可以把遇到问题的设备寄给我,可以更容易的复现问题。
+5. 你可以把遇到问题的设备寄给我,可以更容易的兼容设备和解决问题。
 
 
 # 使用帮助
 # 使用帮助
 QQ群: 901799015, ZLM使用文档[https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit)  
 QQ群: 901799015, ZLM使用文档[https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit)  

+ 1 - 1
doc/_content/ability/gis.md

@@ -14,7 +14,7 @@ WVP提供了简单的电子地图用于设备的定位以及移动设备的轨
 PS: 目前的底图仅用用作演示和学习,商用情况请自行购买授权使用。
 PS: 目前的底图仅用用作演示和学习,商用情况请自行购买授权使用。
 
 
 ### 更换底图以及底图配置
 ### 更换底图以及底图配置
-目前WVP支持使用了更换底图,配置文件在web_src/static/js/mapConfig.js,请修改后重新编译前端文件。
+目前WVP支持使用了更换底图,配置文件在web_src/static/js/config.js,请修改后重新编译前端文件。
 ```javascript
 ```javascript
 window.mapParam = {
 window.mapParam = {
   // 开启/关闭地图功能
   // 开启/关闭地图功能

+ 21 - 2
doc/_content/introduction/deployment.md

@@ -27,13 +27,32 @@
 ```shell
 ```shell
 nohup java -jar wvp-pro-*.jar &
 nohup java -jar wvp-pro-*.jar &
 ```
 ```
-war包:  
+**war包:**  
 下载Tomcat后将war包放入webapps中,启动Tomcat以解压war包,停止Tomcat后,删除ROOT目录以及war包,将解压后的war包目录重命名为ROOT,将配置文件中的Server.port配置为与Tomcat端口一致
 下载Tomcat后将war包放入webapps中,启动Tomcat以解压war包,停止Tomcat后,删除ROOT目录以及war包,将解压后的war包目录重命名为ROOT,将配置文件中的Server.port配置为与Tomcat端口一致
 然后启动Tomcat。  
 然后启动Tomcat。  
 **启动ZLM**
 **启动ZLM**
 ```shell
 ```shell
 nohup ./MediaServer -d -m 3 &
 nohup ./MediaServer -d -m 3 &
 ```
 ```
-
+### 前后端分离部署
+前后端部署目前在最新的版本已经支持,请使用3月15日之后的版本部署
+前端编译后的文件在`src/main/resources/static`中,将此目录下的文件部署。
+前后端分离部署最大的问题是跨域的解决,之前版本使用cookie完成登录流程,而cookie是不可以在复杂跨域中使用的。所以当前版本使用JWT生成的TOKEN作为认证凭据,
+部署前端后需要在wvp中配置前端访问的地址以完成跨域流程。   
+**配置前端服务器**
+1. 假如你的服务有公网域名为xxx.com,公网IP为11.11.11.11, 那么你可以在wvp中这样配置:
+```yaml
+user-settings:
+  # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个
+  allowed-origins:
+    - http://xxx.com:8008
+    - http://11.11.11.11:8008
+```
+配置不是必须的,你使用哪个ip/域名访问就配置哪个即可。修改配置后重启wvp以使配置生效。
+2. 在`src/main/resources/static/static/js/config.js`下配置服务器的地址,也就是wvp服务的地址
+```javascript
+window.baseUrl = "http://xxx.com:18080"
+```
+`这里的地址是需要客户电脑能访问到的,因为请求是客户端电脑发起,与代理不同`  
 [接入设备](./_content/ability/device.md)
 [接入设备](./_content/ability/device.md)
 
 

+ 14 - 11
pom.xml

@@ -11,7 +11,7 @@
 
 
 	<groupId>com.genersoft</groupId>
 	<groupId>com.genersoft</groupId>
 	<artifactId>wvp-pro</artifactId>
 	<artifactId>wvp-pro</artifactId>
-	<version>2.6.7</version>
+	<version>2.6.8</version>
 	<name>web video platform</name>
 	<name>web video platform</name>
 	<description>国标28181视频平台</description>
 	<description>国标28181视频平台</description>
 	<packaging>${project.packaging}</packaging>
 	<packaging>${project.packaging}</packaging>
@@ -123,11 +123,9 @@
 			<artifactId>spring-boot-starter-security</artifactId>
 			<artifactId>spring-boot-starter-security</artifactId>
 		</dependency>
 		</dependency>
 
 
-		<!-- druid数据库连接池 -->
 		<dependency>
 		<dependency>
-			<groupId>com.alibaba</groupId>
-			<artifactId>druid-spring-boot-starter</artifactId>
-			<version>1.2.11</version>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-jdbc</artifactId>
 		</dependency>
 		</dependency>
 
 
 		<!-- mysql数据库 -->
 		<!-- mysql数据库 -->
@@ -216,8 +214,6 @@
 			<version>4.10.0</version>
 			<version>4.10.0</version>
 		</dependency>
 		</dependency>
 
 
-
-
 		<!-- okhttp-digest -->
 		<!-- okhttp-digest -->
 		<dependency>
 		<dependency>
 			<groupId>io.github.rburgst</groupId>
 			<groupId>io.github.rburgst</groupId>
@@ -226,10 +222,17 @@
 		</dependency>
 		</dependency>
 
 
 		<!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 -->
 		<!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 -->
+<!--		<dependency>-->
+<!--			<groupId>net.sf.kxml</groupId>-->
+<!--			<artifactId>kxml2</artifactId>-->
+<!--			<version>2.3.0</version>-->
+<!--		</dependency>-->
+
+		<!-- jwt实现 -->
 		<dependency>
 		<dependency>
-			<groupId>net.sf.kxml</groupId>
-			<artifactId>kxml2</artifactId>
-			<version>2.3.0</version>
+			<groupId>org.bitbucket.b_c</groupId>
+			<artifactId>jose4j</artifactId>
+			<version>0.9.3</version>
 		</dependency>
 		</dependency>
 
 
 		<!--反向代理-->
 		<!--反向代理-->
@@ -289,7 +292,7 @@
 			<plugin>
 			<plugin>
 				<groupId>org.springframework.boot</groupId>
 				<groupId>org.springframework.boot</groupId>
 				<artifactId>spring-boot-maven-plugin</artifactId>
 				<artifactId>spring-boot-maven-plugin</artifactId>
-				<version>2.3.5.RELEASE</version>
+				<version>2.7.2</version>
 				<configuration>
 				<configuration>
 					<includeSystemScope>true</includeSystemScope>
 					<includeSystemScope>true</includeSystemScope>
 				</configuration>
 				</configuration>

+ 12 - 0
sql/2.6.6-2.6.7更新.sql

@@ -0,0 +1,12 @@
+alter table device
+    add asMessageChannel int default 0;
+
+alter table parent_platform
+    add asMessageChannel int default 0;
+
+alter table device
+    add mediaServerId varchar(50) default null;
+
+
+
+

+ 3 - 0
src/main/resources/db/migration/V2.6.7_20230201__初始化.sql

@@ -32,6 +32,7 @@ CREATE TABLE `device` (
                           `transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `streamMode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `streamMode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `online` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `online` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                          `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `registerTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `registerTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `keepaliveTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `keepaliveTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
@@ -47,6 +48,7 @@ CREATE TABLE `device` (
                           `mobilePositionSubmissionInterval` int DEFAULT '5',
                           `mobilePositionSubmissionInterval` int DEFAULT '5',
                           `subscribeCycleForAlarm` int DEFAULT NULL,
                           `subscribeCycleForAlarm` int DEFAULT NULL,
                           `ssrcCheck` int DEFAULT '0',
                           `ssrcCheck` int DEFAULT '0',
+                          `asMessageChannel` int DEFAULT '0',
                           `geoCoordSys` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                           `geoCoordSys` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                           `treeType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                           `treeType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                           `custom_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `custom_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
@@ -329,6 +331,7 @@ CREATE TABLE `parent_platform` (
                                    `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                                    `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                                    `ptz` int DEFAULT NULL,
                                    `ptz` int DEFAULT NULL,
                                    `rtcp` int DEFAULT NULL,
                                    `rtcp` int DEFAULT NULL,
+                                   `asMessageChannel` int DEFAULT '0',
                                    `status` bit(1) DEFAULT NULL,
                                    `status` bit(1) DEFAULT NULL,
                                    `startOfflinePush` int DEFAULT '0',
                                    `startOfflinePush` int DEFAULT '0',
                                    `administrativeDivision` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                                    `administrativeDivision` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,

+ 0 - 2
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java

@@ -1,6 +1,5 @@
 package com.genersoft.iot.vmp;
 package com.genersoft.iot.vmp;
 
 
-import com.genersoft.iot.vmp.conf.druid.EnableDruidSupport;
 import com.genersoft.iot.vmp.utils.GitUtil;
 import com.genersoft.iot.vmp.utils.GitUtil;
 import com.genersoft.iot.vmp.utils.SpringBeanFactory;
 import com.genersoft.iot.vmp.utils.SpringBeanFactory;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
@@ -25,7 +24,6 @@ import java.util.Collections;
 @ServletComponentScan("com.genersoft.iot.vmp.conf")
 @ServletComponentScan("com.genersoft.iot.vmp.conf")
 @SpringBootApplication
 @SpringBootApplication
 @EnableScheduling
 @EnableScheduling
-@EnableDruidSupport
 public class VManageBootstrap extends SpringBootServletInitializer {
 public class VManageBootstrap extends SpringBootServletInitializer {
 
 
 	private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class);
 	private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class);

+ 8 - 4
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java

@@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.common;
 import io.swagger.v3.oas.annotations.media.Schema;
 import io.swagger.v3.oas.annotations.media.Schema;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
+import java.util.Objects;
 
 
 @Schema(description = "流信息")
 @Schema(description = "流信息")
 public class StreamInfo implements Serializable, Cloneable{
 public class StreamInfo implements Serializable, Cloneable{
@@ -168,7 +169,7 @@ public class StreamInfo implements Serializable, Cloneable{
     }
     }
 
 
     public void setRtmp(String host, int port, int sslPort, String app, String stream, String callIdParam) {
     public void setRtmp(String host, int port, int sslPort, String app, String stream, String callIdParam) {
-        String file = String.format("%s/%s/%s", app, stream, callIdParam);
+        String file = String.format("%s/%s%s", app, stream, callIdParam);
         if (port > 0) {
         if (port > 0) {
             this.rtmp = new StreamURL("rtmp", host, port, file);
             this.rtmp = new StreamURL("rtmp", host, port, file);
         }
         }
@@ -178,7 +179,7 @@ public class StreamInfo implements Serializable, Cloneable{
     }
     }
 
 
     public void setRtsp(String host, int port, int sslPort, String app, String stream, String callIdParam) {
     public void setRtsp(String host, int port, int sslPort, String app, String stream, String callIdParam) {
-        String file = String.format("%s/%s/%s", app, stream, callIdParam);
+        String file = String.format("%s/%s%s", app, stream, callIdParam);
         if (port > 0) {
         if (port > 0) {
             this.rtsp = new StreamURL("rtsp", host, port, file);
             this.rtsp = new StreamURL("rtsp", host, port, file);
         }
         }
@@ -236,8 +237,11 @@ public class StreamInfo implements Serializable, Cloneable{
         }
         }
     }
     }
 
 
-    public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam, boolean isPlay) {
-        String file = String.format("index/api/webrtc?app=%s&stream=%s&type=%s%s", app, stream, isPlay?"play":"push", callIdParam);
+    public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam) {
+        if (callIdParam != null) {
+            callIdParam = Objects.equals(callIdParam, "") ? callIdParam : callIdParam.replace("?", "&");
+        }
+        String file = String.format("index/api/webrtc?app=%s&stream=%s&type=play%s", app, stream, callIdParam);
         if (port > 0) {
         if (port > 0) {
             this.rtc = new StreamURL("http", host, port, file);
             this.rtc = new StreamURL("http", host, port, file);
         }
         }

+ 1 - 3
src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java

@@ -66,9 +66,7 @@ public class ApiAccessFilter extends OncePerRequestFilter {
             logDto.setUri(servletRequest.getRequestURI());
             logDto.setUri(servletRequest.getRequestURI());
             logDto.setCreateTime(DateUtil.getNow());
             logDto.setCreateTime(DateUtil.getNow());
             logService.add(logDto);
             logService.add(logDto);
-//            logger.warn("[Api Access]  [{}] [{}] [{}] [{}] [{}] {}ms",
-//                    uriName, servletRequest.getMethod(), servletRequest.getRequestURI(), servletRequest.getRemoteAddr(), HttpStatus.valueOf(servletResponse.getStatus()),
-//                    System.currentTimeMillis() - start);
+
 
 
         }
         }
     }
     }

+ 23 - 0
src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java

@@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.authentication.BadCredentialsException;
 import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.ResponseStatus;
 import org.springframework.web.bind.annotation.ResponseStatus;
 import org.springframework.web.bind.annotation.RestControllerAdvice;
 import org.springframework.web.bind.annotation.RestControllerAdvice;
@@ -32,6 +33,28 @@ public class GlobalExceptionHandler {
         return WVPResult.fail(ErrorCode.ERROR500.getCode(), e.getMessage());
         return WVPResult.fail(ErrorCode.ERROR500.getCode(), e.getMessage());
     }
     }
 
 
+    /**
+     * 默认异常处理
+     * @param e 异常
+     * @return 统一返回结果
+     */
+    @ExceptionHandler(IllegalStateException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public WVPResult<String> exceptionHandler(IllegalStateException e) {
+        return WVPResult.fail(ErrorCode.ERROR400);
+    }
+
+    /**
+     * 默认异常处理
+     * @param e 异常
+     * @return 统一返回结果
+     */
+    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public WVPResult<String> exceptionHandler(HttpRequestMethodNotSupportedException e) {
+        return WVPResult.fail(ErrorCode.ERROR400);
+    }
+
 
 
     /**
     /**
      * 自定义异常处理, 处理controller中返回的错误
      * 自定义异常处理, 处理controller中返回的错误

+ 0 - 3
src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java

@@ -10,14 +10,11 @@ import org.springframework.context.annotation.Bean;
 import org.springframework.core.MethodParameter;
 import org.springframework.core.MethodParameter;
 import org.springframework.http.MediaType;
 import org.springframework.http.MediaType;
 import org.springframework.http.converter.HttpMessageConverter;
 import org.springframework.http.converter.HttpMessageConverter;
-import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
 import org.springframework.http.server.ServerHttpRequest;
 import org.springframework.http.server.ServerHttpRequest;
 import org.springframework.http.server.ServerHttpResponse;
 import org.springframework.http.server.ServerHttpResponse;
 import org.springframework.web.bind.annotation.RestControllerAdvice;
 import org.springframework.web.bind.annotation.RestControllerAdvice;
 import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
 import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
 
 
-import java.util.List;
-
 /**
 /**
  * 全局统一返回结果
  * 全局统一返回结果
  * @author lin
  * @author lin

+ 15 - 7
src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java

@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.conf;
 
 
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IMediaServerService;
+import org.apache.catalina.connector.ClientAbortException;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpRequest;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpResponse;
@@ -169,13 +170,14 @@ public class ProxyServletConfig {
         protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
         protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
             String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
             String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
             MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
             MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
-            String remoteHost = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort());
-            if (mediaInfo != null) {
-                if (!ObjectUtils.isEmpty(queryStr)) {
-                    queryStr += "&remoteHost=" + remoteHost;
-                }else {
-                    queryStr = "remoteHost=" + remoteHost;
-                }
+            if (mediaInfo == null) {
+                return null;
+            }
+            String remoteHost = String.format("http://%s:%s", mediaInfo.getStreamIp(), mediaInfo.getRecordAssistPort());
+            if (!ObjectUtils.isEmpty(queryStr)) {
+                queryStr += "&remoteHost=" + remoteHost;
+            }else {
+                queryStr = "remoteHost=" + remoteHost;
             }
             }
             return queryStr;
             return queryStr;
         }
         }
@@ -192,6 +194,12 @@ public class ProxyServletConfig {
             } catch (IOException ioException) {
             } catch (IOException ioException) {
                 if (ioException instanceof ConnectException) {
                 if (ioException instanceof ConnectException) {
                     logger.error("录像服务 连接失败");
                     logger.error("录像服务 连接失败");
+                }else if (ioException instanceof ClientAbortException) {
+                    /**
+                     * TODO 使用这个代理库实现代理在遇到代理视频文件时,如果是206结果,会遇到报错蛋市目前功能正常,
+                     * TODO 暂时去除异常处理。后续使用其他代理框架修改测试
+                     */
+
                 }else {
                 }else {
                     logger.error("录像服务 代理失败: ", e);
                     logger.error("录像服务 代理失败: ", e);
                 }
                 }

+ 12 - 1
src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java

@@ -4,7 +4,6 @@ package com.genersoft.iot.vmp.conf;
 import org.junit.jupiter.api.Order;
 import org.junit.jupiter.api.Order;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
-import org.springframework.util.ObjectUtils;
 
 
 @Component
 @Component
 @ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true)
 @ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true)
@@ -13,6 +12,8 @@ public class SipConfig {
 
 
 	private String ip;
 	private String ip;
 
 
+	private String showIp;
+
 	private Integer port;
 	private Integer port;
 
 
 	private String domain;
 	private String domain;
@@ -96,4 +97,14 @@ public class SipConfig {
 		this.alarm = alarm;
 		this.alarm = alarm;
 	}
 	}
 
 
+	public String getShowIp() {
+		if (this.showIp == null) {
+			return this.ip;
+		}
+		return showIp;
+	}
+
+	public void setShowIp(String showIp) {
+		this.showIp = showIp;
+	}
 }
 }

+ 6 - 3
src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java

@@ -40,17 +40,20 @@ public class SipPlatformRunner implements CommandLineRunner {
         List<ParentPlatform> parentPlatforms = storager.queryEnableParentPlatformList(true);
         List<ParentPlatform> parentPlatforms = storager.queryEnableParentPlatformList(true);
 
 
         for (ParentPlatform parentPlatform : parentPlatforms) {
         for (ParentPlatform parentPlatform : parentPlatforms) {
+
+            ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
+
             // 更新缓存
             // 更新缓存
             ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch();
             ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch();
             parentPlatformCatch.setParentPlatform(parentPlatform);
             parentPlatformCatch.setParentPlatform(parentPlatform);
             parentPlatformCatch.setId(parentPlatform.getServerGBId());
             parentPlatformCatch.setId(parentPlatform.getServerGBId());
             redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
             redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
-            // 设置所有平台离线
-            platformService.offline(parentPlatform, true);
             // 取消订阅
             // 取消订阅
-            sipCommanderForPlatform.unregister(parentPlatform, null, (eventResult)->{
+            sipCommanderForPlatform.unregister(parentPlatform, parentPlatformCatchOld.getSipTransactionInfo(), null, (eventResult)->{
                 platformService.login(parentPlatform);
                 platformService.login(parentPlatform);
             });
             });
+            // 设置所有平台离线
+            platformService.offline(parentPlatform, true);
         }
         }
     }
     }
 }
 }

+ 39 - 0
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java

@@ -50,15 +50,22 @@ public class UserSetting {
     private Boolean pushStreamAfterAck = Boolean.FALSE;
     private Boolean pushStreamAfterAck = Boolean.FALSE;
 
 
     private Boolean sipLog = Boolean.FALSE;
     private Boolean sipLog = Boolean.FALSE;
+    private Boolean sendToPlatformsWhenIdLost = Boolean.FALSE;
+
+    private Boolean refuseChannelStatusChannelFormNotify = Boolean.FALSE;
 
 
     private String serverId = "000000";
     private String serverId = "000000";
 
 
+    private String recordPath = null;
+
     private String thirdPartyGBIdReg = "[\\s\\S]*";
     private String thirdPartyGBIdReg = "[\\s\\S]*";
 
 
     private String broadcastForPlatform = "UDP";
     private String broadcastForPlatform = "UDP";
 
 
     private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
     private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
 
 
+    private List<String> allowedOrigins = new ArrayList<>();
+
     public Boolean getSavePositionHistory() {
     public Boolean getSavePositionHistory() {
         return savePositionHistory;
         return savePositionHistory;
     }
     }
@@ -238,4 +245,36 @@ public class UserSetting {
     public void setSipLog(Boolean sipLog) {
     public void setSipLog(Boolean sipLog) {
         this.sipLog = sipLog;
         this.sipLog = sipLog;
     }
     }
+
+    public List<String> getAllowedOrigins() {
+        return allowedOrigins;
+    }
+
+    public void setAllowedOrigins(List<String> allowedOrigins) {
+        this.allowedOrigins = allowedOrigins;
+    }
+
+    public Boolean getSendToPlatformsWhenIdLost() {
+        return sendToPlatformsWhenIdLost;
+    }
+
+    public void setSendToPlatformsWhenIdLost(Boolean sendToPlatformsWhenIdLost) {
+        this.sendToPlatformsWhenIdLost = sendToPlatformsWhenIdLost;
+    }
+
+    public Boolean getRefuseChannelStatusChannelFormNotify() {
+        return refuseChannelStatusChannelFormNotify;
+    }
+
+    public void setRefuseChannelStatusChannelFormNotify(Boolean refuseChannelStatusChannelFormNotify) {
+        this.refuseChannelStatusChannelFormNotify = refuseChannelStatusChannelFormNotify;
+    }
+
+    public String getRecordPath() {
+        return recordPath;
+    }
+
+    public void setRecordPath(String recordPath) {
+        this.recordPath = recordPath;
+    }
 }
 }

+ 0 - 64
src/main/java/com/genersoft/iot/vmp/conf/druid/DruidConfiguration.java

@@ -1,64 +0,0 @@
-package com.genersoft.iot.vmp.conf.druid;
-
-import com.alibaba.druid.support.http.StatViewServlet;
-import com.alibaba.druid.support.http.WebStatFilter;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.web.servlet.FilterRegistrationBean;
-import org.springframework.boot.web.servlet.ServletRegistrationBean;
-import org.springframework.context.annotation.Bean;
-
-import javax.servlet.Filter;
-import javax.servlet.Servlet;
-
-/**
- * druid监控配置
- * @author
- */
-public class DruidConfiguration  {
-
-    @Value("${rj-druid-manage.allow:127.0.0.1}")
-    private String allow;
-
-    @Value("${rj-druid-manage.deny:}")
-    private String deny;
-
-    @Value("${rj-druid-manage.loginUsername:admin}")
-    private String loginUsername;
-
-    @Value("${rj-druid-manage.loginPassword:admin}")
-    private String loginPassword;
-
-    @Value("${rj-druid-manage.resetEnable:false}")
-    private String resetEnable;
-
-    /**
-     * druid监控页面开启
-     */
-    @Bean
-    public ServletRegistrationBean druidServlet() {
-        ServletRegistrationBean<Servlet> servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
-        // IP白名单
-        servletRegistrationBean.addInitParameter("allow", allow);
-        // IP黑名单(共同存在时,deny优先于allow)
-        servletRegistrationBean.addInitParameter("deny", deny);
-        //控制台管理用户
-        servletRegistrationBean.addInitParameter("loginUsername", loginUsername);
-        servletRegistrationBean.addInitParameter("loginPassword", loginPassword);
-        //是否能够重置数据 禁用HTML页面上的“Reset All”功能
-        servletRegistrationBean.addInitParameter("resetEnable", resetEnable);
-        return servletRegistrationBean;
-    }
-
-    /**
-     * druid url监控配置
-     */
-    @Bean
-    public FilterRegistrationBean filterRegistrationBean() {
-        FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>(new WebStatFilter());
-        filterRegistrationBean.addUrlPatterns("/*");
-        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
-        return filterRegistrationBean;
-    }
-
-
-}

+ 0 - 24
src/main/java/com/genersoft/iot/vmp/conf/druid/EnableDruidSupport.java

@@ -1,24 +0,0 @@
-package com.genersoft.iot.vmp.conf.druid;
-
-import org.springframework.boot.web.servlet.ServletComponentScan;
-import org.springframework.context.annotation.Import;
-
-import java.lang.annotation.*;
-
-/**
- * druid监控支持注解
- *
- * @author
- * {@link DruidConfiguration} druid监控页面安全配置支持
- * {@link ServletComponentScan} druid监控页面需要扫描servlet
- */
-@Target(ElementType.TYPE)
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-@Inherited
-@Import({
-        DruidConfiguration.class,
-})
-@ServletComponentScan
-public @interface EnableDruidSupport {
-}

+ 9 - 11
src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java

@@ -1,11 +1,11 @@
 package com.genersoft.iot.vmp.conf.security;
 package com.genersoft.iot.vmp.conf.security;
 
 
 import com.alibaba.fastjson2.JSONObject;
 import com.alibaba.fastjson2.JSONObject;
+import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
-import org.apache.poi.hssf.eventmodel.ERFListener;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.web.AuthenticationEntryPoint;
 import org.springframework.security.web.AuthenticationEntryPoint;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 
 
@@ -18,17 +18,15 @@ import java.io.IOException;
  * @author lin
  * @author lin
  */
  */
 @Component
 @Component
-public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint {
-
-    private final static Logger logger = LoggerFactory.getLogger(DefaultUserDetailsServiceImpl.class);
+public class    AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint {
 
 
     @Override
     @Override
     public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
     public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
-        // 允许跨域
-        response.setHeader("Access-Control-Allow-Origin", "*");
-        // 允许自定义请求头token(允许head跨域)
-        response.setHeader("Access-Control-Allow-Headers", "token, Accept, Origin, X-Requested-With, Content-Type, Last-Modified");
-        response.setHeader("Content-type", "application/json;charset=UTF-8");
+        String jwt = request.getHeader(JwtUtils.getHeader());
+        JwtUser jwtUser = JwtUtils.verifyToken(jwt);
+        String username = jwtUser.getUserName();
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword() );
+        SecurityContextHolder.getContext().setAuthentication(token);
         JSONObject jsonObject = new JSONObject();
         JSONObject jsonObject = new JSONObject();
         jsonObject.put("code", ErrorCode.ERROR401.getCode());
         jsonObject.put("code", ErrorCode.ERROR401.getCode());
         jsonObject.put("msg", ErrorCode.ERROR401.getMsg());
         jsonObject.put("msg", ErrorCode.ERROR401.getMsg());

+ 9 - 6
src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java

@@ -1,7 +1,9 @@
 package com.genersoft.iot.vmp.conf.security;
 package com.genersoft.iot.vmp.conf.security;
 
 
-import java.time.LocalDateTime;
-
+import com.alibaba.excel.util.StringUtils;
+import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
+import com.genersoft.iot.vmp.service.IUserService;
+import com.genersoft.iot.vmp.storager.dao.dto.User;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -10,10 +12,7 @@ import org.springframework.security.core.userdetails.UserDetailsService;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 
 
-import com.alibaba.excel.util.StringUtils;
-import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
-import com.genersoft.iot.vmp.service.IUserService;
-import com.genersoft.iot.vmp.storager.dao.dto.User;
+import java.time.LocalDateTime;
 
 
 /**
 /**
  * 用户登录认证逻辑
  * 用户登录认证逻辑
@@ -45,4 +44,8 @@ public class DefaultUserDetailsServiceImpl implements UserDetailsService {
     }
     }
 
 
 
 
+
+
+
+
 }
 }

+ 0 - 24
src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java

@@ -1,24 +0,0 @@
-package com.genersoft.iot.vmp.conf.security;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.security.web.session.InvalidSessionStrategy;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
-/**
- * 登录超时的处理
- */
-public class InvalidSessionHandler implements InvalidSessionStrategy {
-
-    private final static Logger logger = LoggerFactory.getLogger(InvalidSessionHandler.class);
-
-    @Override
-    public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse httpServletResponse) throws IOException, ServletException {
-        String username = request.getParameter("username");
-        logger.info("[登录超时] - [{}]", username);
-    }
-}

+ 84 - 0
src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java

@@ -0,0 +1,84 @@
+package com.genersoft.iot.vmp.conf.security;
+
+import com.genersoft.iot.vmp.conf.UserSetting;
+import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * jwt token 过滤器
+ */
+
+@Component
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+
+
+    @Autowired
+    private UserSetting userSetting;
+
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
+
+        // 忽略登录请求的token验证
+        String requestURI = request.getRequestURI();
+        if (requestURI.equalsIgnoreCase("/api/user/login")) {
+            chain.doFilter(request, response);
+            return;
+        }
+        if (!userSetting.isInterfaceAuthentication()) {
+            // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
+            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, null, new ArrayList<>() );
+            SecurityContextHolder.getContext().setAuthentication(token);
+            chain.doFilter(request, response);
+            return;
+        }
+        String jwt = request.getHeader(JwtUtils.getHeader());
+        // 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的
+        // 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口
+        if (StringUtils.isBlank(jwt)) {
+            jwt = request.getParameter(JwtUtils.getHeader());
+            if (StringUtils.isBlank(jwt)) {
+                chain.doFilter(request, response);
+                return;
+            }
+        }
+
+        JwtUser jwtUser = JwtUtils.verifyToken(jwt);
+        String username = jwtUser.getUserName();
+        // TODO 处理各个状态
+        switch (jwtUser.getStatus()){
+            case EXPIRED:
+                response.setStatus(400);
+                chain.doFilter(request, response);
+                // 异常
+                return;
+            case EXCEPTION:
+                // 过期
+                response.setStatus(400);
+                chain.doFilter(request, response);
+                return;
+            case EXPIRING_SOON:
+                // 即将过期
+//                return;
+            default:
+        }
+
+        // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
+        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword(), new ArrayList<>() );
+        SecurityContextHolder.getContext().setAuthentication(token);
+        chain.doFilter(request, response);
+    }
+
+}

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 138 - 0
src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java


+ 11 - 2
src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java

@@ -21,7 +21,16 @@ public class LoginSuccessHandler implements AuthenticationSuccessHandler {
 
 
     @Override
     @Override
     public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
     public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
-        String username = request.getParameter("username");
-        logger.info("[登录成功] - [{}]", username);
+//        String username = request.getParameter("username");
+//        httpServletResponse.setContentType("application/json;charset=UTF-8");
+//        // 生成JWT,并放置到请求头中
+//        String jwt = JwtUtils.createToken(authentication.getName(), );
+//        httpServletResponse.setHeader(JwtUtils.getHeader(), jwt);
+//        ServletOutputStream outputStream = httpServletResponse.getOutputStream();
+//        outputStream.write(JSON.toJSONString(ErrorCode.SUCCESS).getBytes(StandardCharsets.UTF_8));
+//        outputStream.flush();
+//        outputStream.close();
+
+//        logger.info("[登录成功] - [{}]", username);
     }
     }
 }
 }

+ 13 - 3
src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java

@@ -1,6 +1,7 @@
 package com.genersoft.iot.vmp.conf.security;
 package com.genersoft.iot.vmp.conf.security;
 
 
 import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
 import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
+import com.genersoft.iot.vmp.storager.dao.dto.User;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.Authentication;
@@ -9,6 +10,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 
 
 import javax.security.sasl.AuthenticationException;
 import javax.security.sasl.AuthenticationException;
+import java.time.LocalDateTime;
 
 
 public class SecurityUtils {
 public class SecurityUtils {
 
 
@@ -25,9 +27,12 @@ public class SecurityUtils {
     public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException {
     public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException {
         //使用security框架自带的验证token生成器  也可以自定义。
         //使用security框架自带的验证token生成器  也可以自定义。
         UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password);
         UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password);
+        //认证 如果失败,这里会自动异常后返回,所以这里不需要判断返回值是否为空,确定是否登录成功
         Authentication authenticate = authenticationManager.authenticate(token);
         Authentication authenticate = authenticationManager.authenticate(token);
-        SecurityContextHolder.getContext().setAuthentication(authenticate);
         LoginUser user = (LoginUser) authenticate.getPrincipal();
         LoginUser user = (LoginUser) authenticate.getPrincipal();
+
+        SecurityContextHolder.getContext().setAuthentication(token);
+
         return user;
         return user;
     }
     }
 
 
@@ -49,8 +54,13 @@ public class SecurityUtils {
         if(authentication!=null){
         if(authentication!=null){
             Object principal = authentication.getPrincipal();
             Object principal = authentication.getPrincipal();
             if(principal!=null && !"anonymousUser".equals(principal)){
             if(principal!=null && !"anonymousUser".equals(principal)){
-                LoginUser user = (LoginUser) authentication.getPrincipal();
-                return user;
+//                LoginUser user = (LoginUser) authentication.getPrincipal();
+
+                String username = (String) principal;
+                User user = new User();
+                user.setUsername(username);
+                LoginUser loginUser = new LoginUser(user, LocalDateTime.now());
+                return loginUser;
             }
             }
         }
         }
         return null;
         return null;

+ 56 - 66
src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java

@@ -15,9 +15,16 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 import org.springframework.security.config.annotation.web.builders.WebSecurity;
 import org.springframework.security.config.annotation.web.builders.WebSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.CorsUtils;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
 
 
-import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
 
 
 /**
 /**
  * 配置Spring Security
  * 配置Spring Security
@@ -56,22 +63,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
      */
      */
     @Autowired
     @Autowired
     private AnonymousAuthenticationEntryPoint anonymousAuthenticationEntryPoint;
     private AnonymousAuthenticationEntryPoint anonymousAuthenticationEntryPoint;
-//    /**
-//     * 超时处理
-//     */
-//    @Autowired
-//    private InvalidSessionHandler invalidSessionHandler;
-
-//    /**
-//     * 顶号处理
-//     */
-//    @Autowired
-//    private SessionInformationExpiredHandler sessionInformationExpiredHandler;
-//    /**
-//     * 登录用户没有权限访问资源
-//     */
-//    @Autowired
-//    private LoginUserAccessDeniedHandler accessDeniedHandler;
+    @Autowired
+    private JwtAuthenticationFilter jwtAuthenticationFilter;
 
 
 
 
     /**
     /**
@@ -80,31 +73,21 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
     @Override
     @Override
     public void configure(WebSecurity web) {
     public void configure(WebSecurity web) {
 
 
-        if (!userSetting.isInterfaceAuthentication()) {
-            web.ignoring().antMatchers("**");
-        }else {
-            // 可以直接访问的静态数据
-            web.ignoring()
-                    .antMatchers("/")
-                    .antMatchers("/#/**")
-                    .antMatchers("/static/**")
-                    .antMatchers("/index.html")
-                    .antMatchers("/doc.html") // "/webjars/**", "/swagger-resources/**", "/v3/api-docs/**"
-                    .antMatchers("/webjars/**")
-                    .antMatchers("/swagger-resources/**")
-                    .antMatchers("/v3/api-docs/**")
-                    .antMatchers("/favicon.ico")
-                    .antMatchers("/js/**");
-            List<String> interfaceAuthenticationExcludes = userSetting.getInterfaceAuthenticationExcludes();
-            for (String interfaceAuthenticationExclude : interfaceAuthenticationExcludes) {
-                if (interfaceAuthenticationExclude.split("/").length < 4 ) {
-                    logger.warn("{}不满足两级目录,已忽略", interfaceAuthenticationExclude);
-                }else {
-                    web.ignoring().antMatchers(interfaceAuthenticationExclude);
-                }
-
-            }
-        }
+        ArrayList<String> matchers = new ArrayList<>();
+        matchers.add("/");
+        matchers.add("/#/**");
+        matchers.add("/static/**");
+        matchers.add("/index.html");
+        matchers.add("/doc.html");
+        matchers.add("/webjars/**");
+        matchers.add("/swagger-resources/**");
+        matchers.add("/v3/api-docs/**");
+        matchers.add("/js/**");
+        matchers.add("/api/device/query/snap/**");
+        matchers.add("/record_proxy/*/**");
+        matchers.addAll(userSetting.getInterfaceAuthenticationExcludes());
+        // 可以直接访问的静态数据
+        web.ignoring().antMatchers(matchers.toArray(new String[0]));
     }
     }
 
 
     /**
     /**
@@ -126,36 +109,43 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 
 
     @Override
     @Override
     protected void configure(HttpSecurity http) throws Exception {
     protected void configure(HttpSecurity http) throws Exception {
-        http.cors().and().csrf().disable();
-        // 设置允许添加静态文件
-        http.headers().contentTypeOptions().disable();
-        http.authorizeRequests()
-                // 放行接口
-                .antMatchers("/api/user/login","/index/hook/**").permitAll()
-                // 除上面外的所有请求全部需要鉴权认证
+        http.headers().contentTypeOptions().disable()
+                .and().cors().configurationSource(configurationSource())
+                .and().csrf().disable()
+                .sessionManagement()
+                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+
+                // 配置拦截规则
+                .and()
+                .authorizeRequests()
+                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
+                .antMatchers(userSetting.getInterfaceAuthenticationExcludes().toArray(new String[0])).permitAll()
+                .antMatchers("/api/user/login","/index/hook/**","/zlm_Proxy/FhTuMYqB2HeCuNOb/record/t/1/2023-03-25/16:35:07-16:35:16-9353.mp4").permitAll()
                 .anyRequest().authenticated()
                 .anyRequest().authenticated()
-                // 异常处理(权限拒绝、登录失效等)
-                .and().exceptionHandling()
-                //匿名用户访问无权限资源时的异常处理
+                // 异常处理
+                .and()
+                .exceptionHandling()
                 .authenticationEntryPoint(anonymousAuthenticationEntryPoint)
                 .authenticationEntryPoint(anonymousAuthenticationEntryPoint)
-//                .accessDeniedHandler(accessDeniedHandler)//登录用户没有权限访问资源
-                // 登入 允许所有用户
-                .and().formLogin().permitAll()
-                //登录成功处理逻辑
-                .successHandler(loginSuccessHandler)
-                //登录失败处理逻辑
-                .failureHandler(loginFailureHandler)
-                // 登出
                 .and().logout().logoutUrl("/api/user/logout").permitAll()
                 .and().logout().logoutUrl("/api/user/logout").permitAll()
-                //登出成功处理逻辑
                 .logoutSuccessHandler(logoutHandler)
                 .logoutSuccessHandler(logoutHandler)
-                .deleteCookies("JSESSIONID")
-                // 会话管理
-//                .and().sessionManagement().invalidSessionStrategy(invalidSessionHandler) // 超时处理
-//                .maximumSessions(1)//同一账号同时登录最大用户数
-//                .expiredSessionStrategy(sessionInformationExpiredHandler) // 顶号处理
         ;
         ;
+        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+
+    }
 
 
+    CorsConfigurationSource configurationSource(){
+        // 配置跨域
+        CorsConfiguration corsConfiguration = new CorsConfiguration();
+        corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
+        corsConfiguration.setAllowedMethods(Arrays.asList("*"));
+        corsConfiguration.setMaxAge(3600L);
+        corsConfiguration.setAllowCredentials(true);
+        corsConfiguration.setAllowedOrigins(userSetting.getAllowedOrigins());
+        corsConfiguration.setExposedHeaders(Arrays.asList(JwtUtils.getHeader()));
+
+        UrlBasedCorsConfigurationSource url = new UrlBasedCorsConfigurationSource();
+        url.registerCorsConfiguration("/**",corsConfiguration);
+        return url;
     }
     }
 
 
     /**
     /**

+ 53 - 0
src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java

@@ -0,0 +1,53 @@
+package com.genersoft.iot.vmp.conf.security.dto;
+
+public class JwtUser {
+
+    public enum TokenStatus{
+        /**
+         * 正常的使用状态
+         */
+        NORMAL,
+        /**
+         * 过期而失效
+         */
+        EXPIRED,
+        /**
+         * 即将过期
+         */
+        EXPIRING_SOON,
+        /**
+         * 异常
+         */
+        EXCEPTION
+    }
+
+    private String userName;
+
+    private String password;
+
+    private TokenStatus status;
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public TokenStatus getStatus() {
+        return status;
+    }
+
+    public void setStatus(TokenStatus status) {
+        this.status = status;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+}

+ 9 - 0
src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java

@@ -19,6 +19,8 @@ public class LoginUser implements UserDetails, CredentialsContainer {
      */
      */
     private User user;
     private User user;
 
 
+    private String accessToken;
+
 
 
     /**
     /**
      * 登录时间
      * 登录时间
@@ -102,4 +104,11 @@ public class LoginUser implements UserDetails, CredentialsContainer {
         return user.getPushKey();
         return user.getPushKey();
     }
     }
 
 
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    public void setAccessToken(String accessToken) {
+        this.accessToken = accessToken;
+    }
 }
 }

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java

@@ -171,7 +171,7 @@ public class DigestServerAuthenticationHelper  {
      */
      */
     public boolean doAuthenticatePlainTextPassword(Request request, String pass) {
     public boolean doAuthenticatePlainTextPassword(Request request, String pass) {
         AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
         AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
-        if ( authHeader == null ) {
+        if ( authHeader == null || authHeader.getRealm() == null) {
             return false;
             return false;
         }
         }
         String realm = authHeader.getRealm().trim();
         String realm = authHeader.getRealm().trim();

+ 23 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java

@@ -188,6 +188,13 @@ public class Device {
 	@Schema(description = "SIP交互IP(设备访问平台的IP)")
 	@Schema(description = "SIP交互IP(设备访问平台的IP)")
 	private String localIp;
 	private String localIp;
 
 
+	@Schema(description = "是否作为消息通道")
+	private boolean asMessageChannel;
+
+	@Schema(description = "设备注册的事务信息")
+	private SipTransactionInfo sipTransactionInfo;
+
+
 	public String getDeviceId() {
 	public String getDeviceId() {
 		return deviceId;
 		return deviceId;
 	}
 	}
@@ -428,4 +435,20 @@ public class Device {
 	public void setKeepaliveIntervalTime(int keepaliveIntervalTime) {
 	public void setKeepaliveIntervalTime(int keepaliveIntervalTime) {
 		this.keepaliveIntervalTime = keepaliveIntervalTime;
 		this.keepaliveIntervalTime = keepaliveIntervalTime;
 	}
 	}
+
+	public boolean isAsMessageChannel() {
+		return asMessageChannel;
+	}
+
+	public void setAsMessageChannel(boolean asMessageChannel) {
+		this.asMessageChannel = asMessageChannel;
+	}
+
+	public SipTransactionInfo getSipTransactionInfo() {
+		return sipTransactionInfo;
+	}
+
+	public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
+		this.sipTransactionInfo = sipTransactionInfo;
+	}
 }
 }

+ 11 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java

@@ -189,6 +189,9 @@ public class ParentPlatform {
     @Schema(description = "树类型 国标规定了两种树的展现方式 行政区划 CivilCode 和业务分组:BusinessGrou")
     @Schema(description = "树类型 国标规定了两种树的展现方式 行政区划 CivilCode 和业务分组:BusinessGrou")
     private String treeType;
     private String treeType;
 
 
+    @Schema(description = "是否作为消息通道")
+    private boolean asMessageChannel;
+
     public Integer getId() {
     public Integer getId() {
         return id;
         return id;
     }
     }
@@ -428,4 +431,12 @@ public class ParentPlatform {
     public void setTreeType(String treeType) {
     public void setTreeType(String treeType) {
         this.treeType = treeType;
         this.treeType = treeType;
     }
     }
+
+    public boolean isAsMessageChannel() {
+        return asMessageChannel;
+    }
+
+    public void setAsMessageChannel(boolean asMessageChannel) {
+        this.asMessageChannel = asMessageChannel;
+    }
 }
 }

+ 10 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java

@@ -16,6 +16,8 @@ public class ParentPlatformCatch {
 
 
     private ParentPlatform parentPlatform;
     private ParentPlatform parentPlatform;
 
 
+    private SipTransactionInfo sipTransactionInfo;
+
     public String getId() {
     public String getId() {
         return id;
         return id;
     }
     }
@@ -55,4 +57,12 @@ public class ParentPlatformCatch {
     public void setCallId(String callId) {
     public void setCallId(String callId) {
         this.callId = callId;
         this.callId = callId;
     }
     }
+
+    public SipTransactionInfo getSipTransactionInfo() {
+        return sipTransactionInfo;
+    }
+
+    public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
+        this.sipTransactionInfo = sipTransactionInfo;
+    }
 }
 }

+ 11 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java

@@ -1,6 +1,8 @@
 package com.genersoft.iot.vmp.gb28181.bean;
 package com.genersoft.iot.vmp.gb28181.bean;
 
 
 
 
+import io.swagger.v3.oas.annotations.media.Schema;
+
 import java.time.Instant;
 import java.time.Instant;
 import java.util.List;
 import java.util.List;
 
 
@@ -9,22 +11,29 @@ import java.util.List;
  * @author: swwheihei
  * @author: swwheihei
  * @date:   2020年5月8日 下午2:05:56     
  * @date:   2020年5月8日 下午2:05:56     
  */
  */
+@Schema(description = "设备录像查询结果信息")
 public class RecordInfo {
 public class RecordInfo {
 
 
+	@Schema(description = "设备编号")
 	private String deviceId;
 	private String deviceId;
 
 
+	@Schema(description = "通道编号")
 	private String channelId;
 	private String channelId;
 
 
+	@Schema(description = "命令序列号")
 	private String sn;
 	private String sn;
 
 
+	@Schema(description = "设备名称")
 	private String name;
 	private String name;
-	
+
+	@Schema(description = "列表总数")
 	private int sumNum;
 	private int sumNum;
 
 
 	private int count;
 	private int count;
 
 
 	private Instant lastTime;
 	private Instant lastTime;
-	
+
+	@Schema(description = "")
 	private List<RecordItem> recordList;
 	private List<RecordItem> recordList;
 
 
 	public String getDeviceId() {
 	public String getDeviceId() {

+ 19 - 8
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java

@@ -2,9 +2,9 @@ package com.genersoft.iot.vmp.gb28181.bean;
 
 
 
 
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.utils.DateUtil;
+import io.swagger.v3.oas.annotations.media.Schema;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.NotNull;
 
 
-import java.text.ParseException;
 import java.time.Instant;
 import java.time.Instant;
 import java.time.temporal.TemporalAccessor;
 import java.time.temporal.TemporalAccessor;
 
 
@@ -13,26 +13,37 @@ import java.time.temporal.TemporalAccessor;
  * @author: swwheihei
  * @author: swwheihei
  * @date:   2020年5月8日 下午2:06:54     
  * @date:   2020年5月8日 下午2:06:54     
  */
  */
+@Schema(description = "设备录像详情")
 public class RecordItem  implements Comparable<RecordItem>{
 public class RecordItem  implements Comparable<RecordItem>{
 
 
+	@Schema(description = "设备编号")
 	private String deviceId;
 	private String deviceId;
-	
+
+	@Schema(description = "名称")
 	private String name;
 	private String name;
-	
+
+	@Schema(description = "文件路径名 (可选)")
 	private String filePath;
 	private String filePath;
 
 
+	@Schema(description = "录像文件大小,单位:Byte(可选)")
 	private String fileSize;
 	private String fileSize;
 
 
+	@Schema(description = "录像地址(可选)")
 	private String address;
 	private String address;
-	
+
+	@Schema(description = "录像开始时间(可选)")
 	private String startTime;
 	private String startTime;
-	
+
+	@Schema(description = "录像结束时间(可选)")
 	private String endTime;
 	private String endTime;
-	
+
+	@Schema(description = "保密属性(必选)缺省为0;0:不涉密,1:涉密")
 	private int secrecy;
 	private int secrecy;
-	
+
+	@Schema(description = "录像产生类型(可选)time或alarm 或 manua")
 	private String type;
 	private String type;
-	
+
+	@Schema(description = "录像触发者ID(可选)")
 	private String recorderId;
 	private String recorderId;
 
 
 	public String getDeviceId() {
 	public String getDeviceId() {

+ 1 - 4
src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java

@@ -1,7 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.event.device;
 package com.genersoft.iot.vmp.gb28181.event.device;
 
 
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
-import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.service.IDeviceService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.ApplicationListener;
 import org.springframework.context.ApplicationListener;
@@ -9,8 +8,6 @@ import org.springframework.stereotype.Component;
 
 
 import javax.sip.ClientTransaction;
 import javax.sip.ClientTransaction;
 import javax.sip.address.SipURI;
 import javax.sip.address.SipURI;
-import javax.sip.header.CallIdHeader;
-import javax.sip.header.ToHeader;
 import javax.sip.message.Request;
 import javax.sip.message.Request;
 
 
 /**
 /**
@@ -34,7 +31,7 @@ public class RequestTimeoutEventImpl implements ApplicationListener<RequestTimeo
                 if (device == null) {
                 if (device == null) {
                     return;
                     return;
                 }
                 }
-                deviceService.offline(device.getDeviceId());
+                deviceService.offline(device.getDeviceId(), "等待消息超时");
             }
             }
 
 
         }
         }

+ 2 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java

@@ -61,9 +61,9 @@ public class SipRunner implements CommandLineRunner {
 
 
         for (Device device : deviceList) {
         for (Device device : deviceList) {
             if (deviceService.expire(device)){
             if (deviceService.expire(device)){
-                deviceService.offline(device.getDeviceId());
+                deviceService.offline(device.getDeviceId(), "注册已过期");
             }else {
             }else {
-                deviceService.online(device);
+                deviceService.online(device, null);
             }
             }
         }
         }
         // 重置cseq计数
         // 重置cseq计数

+ 1 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java

@@ -120,7 +120,7 @@ public interface ISIPCommander {
 	 */ 
 	 */ 
 	void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
 	void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
 						   String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
 						   String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
-						   SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
+						   SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
 
 
 
 
 	/**
 	/**
@@ -217,7 +217,6 @@ public interface ISIPCommander {
 	 *
 	 *
 	 * @param device      视频设备
 	 * @param device      视频设备
 	 * @param channelId      通道id,非通道则是设备本身
 	 * @param channelId      通道id,非通道则是设备本身
-	 * @param frontCmd     上级平台的指令,如果存在则直接下发
 	 * @param enabled     看守位使能:1 = 开启,0 = 关闭
 	 * @param enabled     看守位使能:1 = 开启,0 = 关闭
 	 * @param resetTime   自动归位时间间隔,开启看守位时使用,单位:秒(s)
 	 * @param resetTime   自动归位时间间隔,开启看守位时使用,单位:秒(s)
 	 * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
 	 * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255

+ 4 - 7
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java

@@ -22,12 +22,10 @@ public interface ISIPCommanderForPlatform {
      * @param parentPlatform
      * @param parentPlatform
      * @return
      * @return
      */
      */
-    void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent)
-            throws InvalidArgumentException, ParseException, SipException;
+    void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
 
 
-    void register(ParentPlatform parentPlatform, String callId, WWWAuthenticateHeader www,
-                  SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister)
-            throws SipException, InvalidArgumentException, ParseException;
+    void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
+    void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException;
 
 
     /**
     /**
      * 向上级平台注销
      * 向上级平台注销
@@ -35,8 +33,7 @@ public interface ISIPCommanderForPlatform {
      * @param parentPlatform
      * @param parentPlatform
      * @return
      * @return
      */
      */
-    void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent)
-            throws InvalidArgumentException, ParseException, SipException;
+    void unregister(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
 
 
 
 
     /**
     /**

+ 14 - 9
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java

@@ -46,7 +46,7 @@ public class SIPRequestHeaderPlarformProvider {
 	@Autowired
 	@Autowired
 	private IRedisCatchStorage redisCatchStorage;
 	private IRedisCatchStorage redisCatchStorage;
 
 
-	public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, long CSeq, String fromTag, String viaTag, CallIdHeader callIdHeader, boolean isRegister) throws ParseException, InvalidArgumentException, PeerUnavailableException {
+	public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, long CSeq, String fromTag, String toTag, CallIdHeader callIdHeader, boolean isRegister) throws ParseException, InvalidArgumentException, PeerUnavailableException {
 		Request request = null;
 		Request request = null;
 		String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort();
 		String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort();
 		//请求行
 		//请求行
@@ -54,7 +54,8 @@ public class SIPRequestHeaderPlarformProvider {
 				parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
 				parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
 		//via
 		//via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
-		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(), parentPlatform.getServerPort(), parentPlatform.getTransport(), viaTag);
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(),
+				parentPlatform.getServerPort(), parentPlatform.getTransport(), SipUtils.getNewViaTag());
 		viaHeader.setRPort();
 		viaHeader.setRPort();
 		viaHeaders.add(viaHeader);
 		viaHeaders.add(viaHeader);
 		//from
 		//from
@@ -64,7 +65,7 @@ public class SIPRequestHeaderPlarformProvider {
 		//to
 		//to
 		SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
 		SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
 		Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
 		Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
-		ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null);
+		ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,toTag);
 
 
 		//Forwards
 		//Forwards
 		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
 		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
@@ -86,15 +87,21 @@ public class SIPRequestHeaderPlarformProvider {
 		return request;
 		return request;
 	}
 	}
 
 
-	public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, String fromTag, String viaTag,
-										 String callId, WWWAuthenticateHeader www , CallIdHeader callIdHeader, boolean isRegister) throws ParseException, PeerUnavailableException, InvalidArgumentException {
+	public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, String fromTag, String toTag,
+										 WWWAuthenticateHeader www , CallIdHeader callIdHeader, boolean isRegister) throws ParseException, PeerUnavailableException, InvalidArgumentException {
 
 
 
 
-		Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(), fromTag, viaTag, callIdHeader, isRegister);
+		Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(), fromTag, toTag, callIdHeader, isRegister);
 		SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
 		SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
 		if (www == null) {
 		if (www == null) {
 			AuthorizationHeader authorizationHeader = sipLayer.getSipFactory().createHeaderFactory().createAuthorizationHeader("Digest");
 			AuthorizationHeader authorizationHeader = sipLayer.getSipFactory().createHeaderFactory().createAuthorizationHeader("Digest");
-			authorizationHeader.setUsername(parentPlatform.getDeviceGBId());
+			String username = parentPlatform.getUsername();
+			if ( username == null || username == "" )
+			{
+				authorizationHeader.setUsername(parentPlatform.getDeviceGBId());
+			} else {
+				authorizationHeader.setUsername(username);
+			}
 			authorizationHeader.setURI(requestURI);
 			authorizationHeader.setURI(requestURI);
 			authorizationHeader.setAlgorithm("MD5");
 			authorizationHeader.setAlgorithm("MD5");
 			registerRequest.addHeader(authorizationHeader);
 			registerRequest.addHeader(authorizationHeader);
@@ -108,8 +115,6 @@ public class SIPRequestHeaderPlarformProvider {
 		// qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
 		// qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
 		String qop = www.getQop();
 		String qop = www.getQop();
 
 
-		callIdHeader.setCallId(callId);
-
 		String cNonce = null;
 		String cNonce = null;
 		String nc = "00000001";
 		String nc = "00000001";
 		if (qop != null) {
 		if (qop != null) {

+ 30 - 19
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java

@@ -276,7 +276,7 @@ public class SIPCommander implements ISIPCommander {
             return;
             return;
         }
         }
 
 
-        logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
+        logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
         HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
         HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
         subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
         subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
             if (event != null) {
             if (event != null) {
@@ -377,7 +377,7 @@ public class SIPCommander implements ISIPCommander {
                                   SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
                                   SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
 
 
 
 
-        logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
+        logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getSdpIp(), mediaServerItem.getIp(), ssrcInfo.getPort());
         String sdpIp;
         String sdpIp;
         if (!ObjectUtils.isEmpty(device.getSdpIp())) {
         if (!ObjectUtils.isEmpty(device.getSdpIp())) {
             sdpIp = device.getSdpIp();
             sdpIp = device.getSdpIp();
@@ -479,10 +479,11 @@ public class SIPCommander implements ISIPCommander {
      */
      */
     @Override
     @Override
     public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
     public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
-                                  String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
-                                  SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+                                  String startTime, String endTime, int downloadSpeed,
+                                  InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
+                                  SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
 
 
-        logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
+        logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getSdpIp(), mediaServerItem.getIp(), ssrcInfo.getPort());
         String sdpIp;
         String sdpIp;
         if (!ObjectUtils.isEmpty(device.getSdpIp())) {
         if (!ObjectUtils.isEmpty(device.getSdpIp())) {
             sdpIp = device.getSdpIp();
             sdpIp = device.getSdpIp();
@@ -549,11 +550,14 @@ public class SIPCommander implements ISIPCommander {
         content.append("a=downloadspeed:" + downloadSpeed + "\r\n");
         content.append("a=downloadspeed:" + downloadSpeed + "\r\n");
 
 
         content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
         content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
-
+        logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc());
         HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId());
         HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId());
         // 添加订阅
         // 添加订阅
+        CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
+        String callId=newCallIdHeader.getCallId();
         subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
         subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
-            hookEvent.call(new InviteStreamInfo(mediaServerItem, json,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream()));
+            logger.debug("sipc 添加订阅===callId {}",callId);
+            hookEvent.call(new InviteStreamInfo(mediaServerItem, json,callId, "rtp", ssrcInfo.getStream()));
             subscribe.removeSubscribe(hookSubscribe);
             subscribe.removeSubscribe(hookSubscribe);
             hookSubscribe.getContent().put("regist", false);
             hookSubscribe.getContent().put("regist", false);
             hookSubscribe.getContent().put("schema", "rtsp");
             hookSubscribe.getContent().put("schema", "rtsp");
@@ -562,7 +566,7 @@ public class SIPCommander implements ISIPCommander {
                     (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd) -> {
                     (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd) -> {
                         logger.info("[录像]下载结束, 发送BYE");
                         logger.info("[录像]下载结束, 发送BYE");
                         try {
                         try {
-                            streamByeCmd(device, channelId, ssrcInfo.getStream(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId());
+                            streamByeCmd(device, channelId, ssrcInfo.getStream(),callId);
                         } catch (InvalidArgumentException | ParseException | SipException |
                         } catch (InvalidArgumentException | ParseException | SipException |
                                  SsrcTransactionNotFoundException e) {
                                  SsrcTransactionNotFoundException e) {
                             logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage());
                             logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage());
@@ -570,15 +574,24 @@ public class SIPCommander implements ISIPCommander {
                     });
                     });
         });
         });
 
 
-        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc());
+        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,newCallIdHeader, ssrcInfo.getSsrc());
         if (inviteStreamCallback != null) {
         if (inviteStreamCallback != null) {
-            inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream()));
+            inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,callId, "rtp", ssrcInfo.getStream()));
         }
         }
 
 
-        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent -> {
-            ResponseEvent responseEvent = (ResponseEvent) okEvent.event;
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> {
+            ResponseEvent responseEvent = (ResponseEvent) event.event;
             SIPResponse response = (SIPResponse) responseEvent.getResponse();
             SIPResponse response = (SIPResponse) responseEvent.getResponse();
-            streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download);
+            String contentString =new String(response.getRawContent());
+            int ssrcIndex = contentString.indexOf("y=");
+            String ssrc=ssrcInfo.getSsrc();
+            if (ssrcIndex >= 0) {
+                ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
+            }
+            logger.debug("接收到的下载响应ssrc====>{}",ssrcInfo.getSsrc());
+            logger.debug("接收到的下载响应ssrc====>{}",ssrc);
+            streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download);
+            okEvent.response(event);
         });
         });
     }
     }
 
 
@@ -778,7 +791,7 @@ public class SIPCommander implements ISIPCommander {
         cmdXml.append("<GuardCmd>" + guardCmdStr + "</GuardCmd>\r\n");
         cmdXml.append("<GuardCmd>" + guardCmdStr + "</GuardCmd>\r\n");
         cmdXml.append("</Control>\r\n");
         cmdXml.append("</Control>\r\n");
 
 
-        
+
 
 
         Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
         Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
         sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
         sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
@@ -854,7 +867,6 @@ public class SIPCommander implements ISIPCommander {
      *
      *
      * @param device      视频设备
      * @param device      视频设备
      * @param channelId      通道id,非通道则是设备本身
      * @param channelId      通道id,非通道则是设备本身
-     * @param frontCmd     上级平台的指令,如果存在则直接下发
      * @param enabled     看守位使能:1 = 开启,0 = 关闭
      * @param enabled     看守位使能:1 = 开启,0 = 关闭
      * @param resetTime   自动归位时间间隔,开启看守位时使用,单位:秒(s)
      * @param resetTime   自动归位时间间隔,开启看守位时使用,单位:秒(s)
      * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
      * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
@@ -978,7 +990,7 @@ public class SIPCommander implements ISIPCommander {
         catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
         catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
         catalogXml.append("</Query>\r\n");
         catalogXml.append("</Query>\r\n");
 
 
-
+        
 
 
         Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
         Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
 
 
@@ -1181,7 +1193,6 @@ public class SIPCommander implements ISIPCommander {
         cmdXml.append("</Query>\r\n");
         cmdXml.append("</Query>\r\n");
 
 
 
 
-
         Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
         Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
         sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
         sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
     }
     }
@@ -1427,7 +1438,7 @@ public class SIPCommander implements ISIPCommander {
         if (device == null) {
         if (device == null) {
             return;
             return;
         }
         }
-        logger.info("[发送 报警通知] {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),
+        logger.info("[发送报警通知]设备: {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),
                 deviceAlarm.getLongitude(), deviceAlarm.getLatitude());
                 deviceAlarm.getLongitude(), deviceAlarm.getLatitude());
 
 
         String characterSet = device.getCharset();
         String characterSet = device.getCharset();
@@ -1439,7 +1450,7 @@ public class SIPCommander implements ISIPCommander {
         deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
         deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
         deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
         deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
         deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
         deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
-        deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");
+        deviceStatusXml.append("<AlarmTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(deviceAlarm.getAlarmTime()) + "</AlarmTime>\r\n");
         deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
         deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
         deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
         deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
         deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
         deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");

+ 48 - 16
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java

@@ -24,6 +24,7 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
 import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.utils.DateUtil;
+import com.genersoft.iot.vmp.utils.GitUtil;
 import gov.nist.javax.sip.message.MessageFactoryImpl;
 import gov.nist.javax.sip.message.MessageFactoryImpl;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPResponse;
 import gov.nist.javax.sip.message.SIPResponse;
@@ -85,26 +86,49 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
     @Autowired
     @Autowired
     private DynamicTask dynamicTask;
     private DynamicTask dynamicTask;
 
 
+    @Autowired
+    private GitUtil gitUtil;
+
     @Override
     @Override
     public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
     public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
         register(parentPlatform, null, null, errorEvent, okEvent, false, true);
         register(parentPlatform, null, null, errorEvent, okEvent, false, true);
     }
     }
 
 
     @Override
     @Override
-    public void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
-        register(parentPlatform, null, null, errorEvent, okEvent, false, false);
+    public void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
+
+        register(parentPlatform, sipTransactionInfo, null, errorEvent, okEvent, false, true);
     }
     }
 
 
     @Override
     @Override
-    public void register(ParentPlatform parentPlatform, @Nullable String callId, @Nullable WWWAuthenticateHeader www,
+    public void unregister(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
+        register(parentPlatform, sipTransactionInfo, null, errorEvent, okEvent, false, false);
+    }
+
+    @Override
+    public void register(ParentPlatform parentPlatform, @Nullable SipTransactionInfo sipTransactionInfo, @Nullable WWWAuthenticateHeader www,
                             SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException {
                             SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException {
             Request request;
             Request request;
-            if (!registerAgain ) {
-                CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
 
 
+            CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
+            String fromTag = SipUtils.getNewFromTag();
+            String toTag = null;
+            if (sipTransactionInfo != null ) {
+                if (sipTransactionInfo.getCallId() != null) {
+                    callIdHeader.setCallId(sipTransactionInfo.getCallId());
+                }
+                if (sipTransactionInfo.getFromTag() != null) {
+                    fromTag = sipTransactionInfo.getFromTag();
+                }
+                if (sipTransactionInfo.getToTag() != null) {
+                    toTag = sipTransactionInfo.getToTag();
+                }
+            }
+
+            if (!registerAgain ) {
                 request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform,
                 request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform,
-                        redisCatchStorage.getCSEQ(), SipUtils.getNewFromTag(),
-                        SipUtils.getNewViaTag(), callIdHeader, isRegister);
+                        redisCatchStorage.getCSEQ(), fromTag,
+                        toTag, callIdHeader, isRegister);
                 // 将 callid 写入缓存, 等注册成功可以更新状态
                 // 将 callid 写入缓存, 等注册成功可以更新状态
                 String callIdFromHeader = callIdHeader.getCallId();
                 String callIdFromHeader = callIdHeader.getCallId();
                 redisCatchStorage.updatePlatformRegisterInfo(callIdFromHeader, PlatformRegisterInfo.getInstance(parentPlatform.getServerGBId(), isRegister));
                 redisCatchStorage.updatePlatformRegisterInfo(callIdFromHeader, PlatformRegisterInfo.getInstance(parentPlatform.getServerGBId(), isRegister));
@@ -122,8 +146,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
                 });
                 });
 
 
             }else {
             }else {
-                CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
-                request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, SipUtils.getNewFromTag(), null, callId, www, callIdHeader, isRegister);
+                request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, fromTag, toTag, www, callIdHeader, isRegister);
             }
             }
 
 
             sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, okEvent);
             sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, okEvent);
@@ -245,6 +268,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
                         catalogXml.append("<IPAddress>" + channel.getIpAddress() + "</IPAddress>\r\n");
                         catalogXml.append("<IPAddress>" + channel.getIpAddress() + "</IPAddress>\r\n");
                         catalogXml.append("<Port>" + channel.getPort() + "</Port>\r\n");
                         catalogXml.append("<Port>" + channel.getPort() + "</Port>\r\n");
                         catalogXml.append("<Password>" + channel.getPort() + "</Password>\r\n");
                         catalogXml.append("<Password>" + channel.getPort() + "</Password>\r\n");
+                        catalogXml.append("<PTZType>" + channel.getPTZType() + "</PTZType>\r\n");
                         catalogXml.append("<Status>" + (channel.getStatus() == 1?"ON":"OFF") + "</Status>\r\n");
                         catalogXml.append("<Status>" + (channel.getStatus() == 1?"ON":"OFF") + "</Status>\r\n");
                         catalogXml.append("<Longitude>" +
                         catalogXml.append("<Longitude>" +
                                 (channel.getLongitudeWgs84() != 0? channel.getLongitudeWgs84():channel.getLongitude())
                                 (channel.getLongitudeWgs84() != 0? channel.getLongitudeWgs84():channel.getLongitude())
@@ -285,6 +309,9 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
 
 
         String callId = request.getCallIdHeader().getCallId();
         String callId = request.getCallIdHeader().getCallId();
 
 
+        logger.info("[命令发送] 国标级联{} 目录查询回复: 共{}条,已发送{}条", parentPlatform.getServerGBId(),
+                channels.size(), Math.min(index + parentPlatform.getCatalogGroup(), channels.size()));
+        logger.debug(catalogXml);
         if (sendAfterResponse) {
         if (sendAfterResponse) {
             // 默认按照收到200回复后发送下一条, 如果超时收不到回复,就以30毫秒的间隔直接发送。
             // 默认按照收到200回复后发送下一条, 如果超时收不到回复,就以30毫秒的间隔直接发送。
             dynamicTask.startDelay(timeoutTaskKey, ()->{
             dynamicTask.startDelay(timeoutTaskKey, ()->{
@@ -336,17 +363,22 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         if (parentPlatform == null) {
         if (parentPlatform == null) {
             return;
             return;
         }
         }
+        String deviceId = device == null ? parentPlatform.getDeviceGBId() : device.getDeviceId();
+        String deviceName = device == null ? parentPlatform.getName() : device.getName();
+        String manufacturer = device == null ? "WVP-28181-PRO" : device.getManufacturer();
+        String model = device == null ? "platform" : device.getModel();
+        String firmware = device == null ? gitUtil.getBuildVersion() : device.getFirmware();
         String characterSet = parentPlatform.getCharacterSet();
         String characterSet = parentPlatform.getCharacterSet();
         StringBuffer deviceInfoXml = new StringBuffer(600);
         StringBuffer deviceInfoXml = new StringBuffer(600);
         deviceInfoXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
         deviceInfoXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
         deviceInfoXml.append("<Response>\r\n");
         deviceInfoXml.append("<Response>\r\n");
         deviceInfoXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
         deviceInfoXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
         deviceInfoXml.append("<SN>" +sn + "</SN>\r\n");
         deviceInfoXml.append("<SN>" +sn + "</SN>\r\n");
-        deviceInfoXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
-        deviceInfoXml.append("<DeviceName>" + device.getName() + "</DeviceName>\r\n");
-        deviceInfoXml.append("<Manufacturer>" + device.getManufacturer() + "</Manufacturer>\r\n");
-        deviceInfoXml.append("<Model>" + device.getModel() + "</Model>\r\n");
-        deviceInfoXml.append("<Firmware>" + device.getFirmware() + "</Firmware>\r\n");
+        deviceInfoXml.append("<DeviceID>" + deviceId + "</DeviceID>\r\n");
+        deviceInfoXml.append("<DeviceName>" + deviceName + "</DeviceName>\r\n");
+        deviceInfoXml.append("<Manufacturer>" + manufacturer + "</Manufacturer>\r\n");
+        deviceInfoXml.append("<Model>" + model + "</Model>\r\n");
+        deviceInfoXml.append("<Firmware>" + firmware + "</Firmware>\r\n");
         deviceInfoXml.append("<Result>OK</Result>\r\n");
         deviceInfoXml.append("<Result>OK</Result>\r\n");
         deviceInfoXml.append("</Response>\r\n");
         deviceInfoXml.append("</Response>\r\n");
 
 
@@ -423,7 +455,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         if (parentPlatform == null) {
         if (parentPlatform == null) {
             return;
             return;
         }
         }
-        logger.info("[发送报警通知] {}/{}->{},{}: {}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(),
+        logger.info("[发送报警通知]平台: {}/{}->{},{}: {}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(),
                 deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSON.toJSONString(deviceAlarm));
                 deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSON.toJSONString(deviceAlarm));
         String characterSet = parentPlatform.getCharacterSet();
         String characterSet = parentPlatform.getCharacterSet();
         StringBuffer deviceStatusXml = new StringBuffer(600);
         StringBuffer deviceStatusXml = new StringBuffer(600);
@@ -434,7 +466,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
                 .append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n")
                 .append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n")
                 .append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n")
                 .append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n")
                 .append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n")
                 .append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n")
-                .append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n")
+                .append("<AlarmTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(deviceAlarm.getAlarmTime()) + "</AlarmTime>\r\n")
                 .append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n")
                 .append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n")
                 .append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n")
                 .append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n")
                 .append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n")
                 .append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n")

+ 2 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java

@@ -45,7 +45,7 @@ public abstract class SIPRequestProcessorParent {
 		try {
 		try {
 			return SipFactory.getInstance().createHeaderFactory();
 			return SipFactory.getInstance().createHeaderFactory();
 		} catch (PeerUnavailableException e) {
 		} catch (PeerUnavailableException e) {
-			e.printStackTrace();
+			logger.error("未处理的异常 ", e);
 		}
 		}
 		return null;
 		return null;
 	}
 	}
@@ -54,7 +54,7 @@ public abstract class SIPRequestProcessorParent {
 		try {
 		try {
 			return SipFactory.getInstance().createMessageFactory();
 			return SipFactory.getInstance().createMessageFactory();
 		} catch (PeerUnavailableException e) {
 		} catch (PeerUnavailableException e) {
-			e.printStackTrace();
+			logger.error("未处理的异常 ", e);
 		}
 		}
 		return null;
 		return null;
 	}
 	}

+ 33 - 21
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java

@@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
+import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
@@ -457,12 +458,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                             if (!userSetting.getPushStreamAfterAck()) {
                             if (!userSetting.getPushStreamAfterAck()) {
                                 playService.startPushStream(sendRtpItem, sipResponse, platform, request.getCallIdHeader());
                                 playService.startPushStream(sendRtpItem, sipResponse, platform, request.getCallIdHeader());
                             }
                             }
-                        } catch (SipException e) {
-                            e.printStackTrace();
-                        } catch (InvalidArgumentException e) {
-                            e.printStackTrace();
-                        } catch (ParseException e) {
-                            e.printStackTrace();
+                        } catch (SipException | InvalidArgumentException | ParseException e) {
+                            logger.error("[命令发送失败] 国标级联 回复SdpAck", e);
                         }
                         }
                     };
                     };
                     SipSubscribe.Event errorEvent = ((event) -> {
                     SipSubscribe.Event errorEvent = ((event) -> {
@@ -471,7 +468,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                             Response response = getMessageFactory().createResponse(event.statusCode, evt.getRequest());
                             Response response = getMessageFactory().createResponse(event.statusCode, evt.getRequest());
                             sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
                             sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
                         } catch (ParseException | SipException  e) {
                         } catch (ParseException | SipException  e) {
-                            e.printStackTrace();
+                            logger.error("未处理的异常 ", e);
                         }
                         }
                     });
                     });
                     sendRtpItem.setApp("rtp");
                     sendRtpItem.setApp("rtp");
@@ -543,6 +540,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                         }
                         }
                     }
                     }
                 } else if (gbStream != null) {
                 } else if (gbStream != null) {
+                    if(ssrc.equals(ssrcDefault))
+                    {
+                        SsrcConfig ssrcConfig = mediaServerItem.getSsrcConfig();
+                        if(ssrcConfig != null)
+                        {
+                            ssrc = ssrcConfig.getPlaySsrc();
+                            ssrcConfig.releaseSsrc(ssrc);
+                        }
+                    }
                     if("push".equals(gbStream.getStreamType())) {
                     if("push".equals(gbStream.getStreamType())) {
                         if (streamPushItem != null && streamPushItem.isPushIng()) {
                         if (streamPushItem != null && streamPushItem.isPushIng()) {
                             // 推流状态
                             // 推流状态
@@ -572,7 +578,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
         } catch (SdpParseException e) {
         } catch (SdpParseException e) {
             logger.error("sdp解析错误", e);
             logger.error("sdp解析错误", e);
         } catch (SdpException e) {
         } catch (SdpException e) {
-            e.printStackTrace();
+            logger.error("未处理的异常 ", e);
         }
         }
     }
     }
 
 
@@ -727,11 +733,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream());
                     mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream());
                     responseAck(request, Response.REQUEST_TIMEOUT); // 超时
                     responseAck(request, Response.REQUEST_TIMEOUT); // 超时
                 } catch (SipException e) {
                 } catch (SipException e) {
-                    e.printStackTrace();
+                    logger.error("未处理的异常 ", e);
                 } catch (InvalidArgumentException e) {
                 } catch (InvalidArgumentException e) {
-                    e.printStackTrace();
+                    logger.error("未处理的异常 ", e);
                 } catch (ParseException e) {
                 } catch (ParseException e) {
-                    e.printStackTrace();
+                    logger.error("未处理的异常 ", e);
                 }
                 }
             }, userSetting.getPlatformPlayTimeout());
             }, userSetting.getPlatformPlayTimeout());
             // 添加监听
             // 添加监听
@@ -750,11 +756,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                         try {
                         try {
                             responseAck(request, Response.BUSY_HERE);
                             responseAck(request, Response.BUSY_HERE);
                         } catch (SipException e) {
                         } catch (SipException e) {
-                            e.printStackTrace();
+                            logger.error("未处理的异常 ", e);
                         } catch (InvalidArgumentException e) {
                         } catch (InvalidArgumentException e) {
-                            e.printStackTrace();
+                            logger.error("未处理的异常 ", e);
                         } catch (ParseException e) {
                         } catch (ParseException e) {
-                            e.printStackTrace();
+                            logger.error("未处理的异常 ", e);
                         }
                         }
                         return;
                         return;
                     }
                     }
@@ -812,11 +818,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                         try {
                         try {
                             responseAck(request, Response.BUSY_HERE);
                             responseAck(request, Response.BUSY_HERE);
                         } catch (SipException e) {
                         } catch (SipException e) {
-                            e.printStackTrace();
+                            logger.error("未处理的异常 ", e);
                         } catch (InvalidArgumentException e) {
                         } catch (InvalidArgumentException e) {
-                            e.printStackTrace();
+                            logger.error("未处理的异常 ", e);
                         } catch (ParseException e) {
                         } catch (ParseException e) {
-                            e.printStackTrace();
+                            logger.error("未处理的异常 ", e);
                         }
                         }
                         return;
                         return;
                     }
                     }
@@ -869,7 +875,13 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
         content.append("s=Play\r\n");
         content.append("s=Play\r\n");
         content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
         content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
         content.append("t=0 0\r\n");
         content.append("t=0 0\r\n");
-        content.append("m=video " + sendRtpItem.getLocalPort() + " RTP/AVP 96\r\n");
+        // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口
+        int localPort = sendRtpItem.getLocalPort();
+        if(localPort == 0)
+        {
+            localPort = new Random().nextInt(65535) + 1;
+        }
+        content.append("m=video " + localPort + " RTP/AVP 96\r\n");
         content.append("a=sendonly\r\n");
         content.append("a=sendonly\r\n");
         content.append("a=rtpmap:96 PS/90000\r\n");
         content.append("a=rtpmap:96 PS/90000\r\n");
         if (sendRtpItem.isTcp()) {
         if (sendRtpItem.isTcp()) {
@@ -890,11 +902,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
             }
             }
             return sipResponse;
             return sipResponse;
         } catch (SipException e) {
         } catch (SipException e) {
-            e.printStackTrace();
+            logger.error("未处理的异常 ", e);
         } catch (InvalidArgumentException e) {
         } catch (InvalidArgumentException e) {
-            e.printStackTrace();
+            logger.error("未处理的异常 ", e);
         } catch (ParseException e) {
         } catch (ParseException e) {
-            e.printStackTrace();
+            logger.error("未处理的异常 ", e);
         }
         }
         return null;
         return null;
     }
     }

+ 14 - 6
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java

@@ -93,7 +93,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
 		try {
 		try {
 			responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null);
 			responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null);
 		}catch (SipException | InvalidArgumentException | ParseException e) {
 		}catch (SipException | InvalidArgumentException | ParseException e) {
-			e.printStackTrace();
+			logger.error("未处理的异常 ", e);
 		}
 		}
 		boolean runed = !taskQueue.isEmpty();
 		boolean runed = !taskQueue.isEmpty();
 		taskQueue.offer(new HandlerCatchData(evt, null, null));
 		taskQueue.offer(new HandlerCatchData(evt, null, null));
@@ -229,7 +229,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
 			jsonObject.put("speed", mobilePosition.getSpeed());
 			jsonObject.put("speed", mobilePosition.getSpeed());
 			redisCatchStorage.sendMobilePositionMsg(jsonObject);
 			redisCatchStorage.sendMobilePositionMsg(jsonObject);
 		} catch (DocumentException  e) {
 		} catch (DocumentException  e) {
-			e.printStackTrace();
+			logger.error("未处理的异常 ", e);
 		}
 		}
 	}
 	}
 
 
@@ -339,7 +339,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
 				publisher.deviceAlarmEventPublish(deviceAlarm);
 				publisher.deviceAlarmEventPublish(deviceAlarm);
 			}
 			}
 		} catch (DocumentException e) {
 		} catch (DocumentException e) {
-			e.printStackTrace();
+			logger.error("未处理的异常 ", e);
 		}
 		}
 	}
 	}
 
 
@@ -397,12 +397,20 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
 						case CatalogEvent.OFF :
 						case CatalogEvent.OFF :
 							// 离线
 							// 离线
 							logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
 							logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
-							storager.deviceChannelOffline(deviceId, channel.getChannelId());
+							if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
+								storager.deviceChannelOffline(deviceId, channel.getChannelId());
+							}else {
+								logger.info("[收到通道离线通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
+							}
 							break;
 							break;
 						case CatalogEvent.VLOST:
 						case CatalogEvent.VLOST:
 							// 视频丢失
 							// 视频丢失
 							logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
 							logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
-							storager.deviceChannelOffline(deviceId, channel.getChannelId());
+							if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
+								storager.deviceChannelOffline(deviceId, channel.getChannelId());
+							}else {
+								logger.info("[收到通道视频丢失通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
+							}
 							break;
 							break;
 						case CatalogEvent.DEFECT:
 						case CatalogEvent.DEFECT:
 							// 故障
 							// 故障
@@ -432,7 +440,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
 				}
 				}
 			}
 			}
 		} catch (DocumentException e) {
 		} catch (DocumentException e) {
-			e.printStackTrace();
+			logger.error("未处理的异常 ", e);
 		}
 		}
 	}
 	}
 
 

+ 50 - 6
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java

@@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
 import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo;
 import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo;
+import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
 import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate;
 import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
@@ -18,6 +19,7 @@ import gov.nist.javax.sip.address.AddressImpl;
 import gov.nist.javax.sip.address.SipUri;
 import gov.nist.javax.sip.address.SipUri;
 import gov.nist.javax.sip.header.SIPDateHeader;
 import gov.nist.javax.sip.header.SIPDateHeader;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPRequest;
+import gov.nist.javax.sip.message.SIPResponse;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.InitializingBean;
@@ -34,6 +36,7 @@ import javax.sip.header.AuthorizationHeader;
 import javax.sip.header.ContactHeader;
 import javax.sip.header.ContactHeader;
 import javax.sip.header.FromHeader;
 import javax.sip.header.FromHeader;
 import javax.sip.header.ViaHeader;
 import javax.sip.header.ViaHeader;
+import javax.sip.message.Request;
 import javax.sip.message.Response;
 import javax.sip.message.Response;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchAlgorithmException;
 import java.text.ParseException;
 import java.text.ParseException;
@@ -105,6 +108,30 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
             SipUri uri = (SipUri) address.getURI();
             SipUri uri = (SipUri) address.getURI();
             String deviceId = uri.getUser();
             String deviceId = uri.getUser();
             Device device = deviceService.getDevice(deviceId);
             Device device = deviceService.getDevice(deviceId);
+
+            RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request,
+                    userSetting.getSipUseSourceIpAsRemoteAddress());
+
+            if (device != null &&
+                device.getSipTransactionInfo() != null &&
+                request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) {
+                logger.info("[注册请求] 注册续订: {}", device.getDeviceId());
+                device.setExpires(request.getExpires().getExpires());
+                device.setIp(remoteAddressInfo.getIp());
+                device.setPort(remoteAddressInfo.getPort());
+                device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
+                device.setLocalIp(request.getLocalAddress().getHostAddress());
+                Response registerOkResponse = getRegisterOkResponse(request);
+                // 判断TCP还是UDP
+                ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
+                String transport = reqViaHeader.getTransport();
+                device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
+                sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), registerOkResponse);
+                device.setRegisterTime(DateUtil.getNow());
+                SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)registerOkResponse);
+                deviceService.online(device, sipTransactionInfo);
+                return;
+            }
             String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword();
             String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword();
             AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
             AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
             if (authHead == null && !ObjectUtils.isEmpty(password)) {
             if (authHead == null && !ObjectUtils.isEmpty(password)) {
@@ -147,9 +174,6 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
             // 添加Expires头
             // 添加Expires头
             response.addHeader(request.getExpires());
             response.addHeader(request.getExpires());
 
 
-            RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request,
-                    userSetting.getSipUseSourceIpAsRemoteAddress());
-
             if (device == null) {
             if (device == null) {
                 device = new Device();
                 device = new Device();
                 device.setStreamMode("UDP");
                 device.setStreamMode("UDP");
@@ -182,13 +206,33 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
             if (registerFlag) {
             if (registerFlag) {
                 logger.info("[注册成功] deviceId: {}->{}",  deviceId, requestAddress);
                 logger.info("[注册成功] deviceId: {}->{}",  deviceId, requestAddress);
                 device.setRegisterTime(DateUtil.getNow());
                 device.setRegisterTime(DateUtil.getNow());
-                deviceService.online(device);
+                SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)response);
+                deviceService.online(device, sipTransactionInfo);
             } else {
             } else {
                 logger.info("[注销成功] deviceId: {}->{}" ,deviceId, requestAddress);
                 logger.info("[注销成功] deviceId: {}->{}" ,deviceId, requestAddress);
-                deviceService.offline(deviceId);
+                deviceService.offline(deviceId, "主动注销");
             }
             }
         } catch (SipException | NoSuchAlgorithmException | ParseException e) {
         } catch (SipException | NoSuchAlgorithmException | ParseException e) {
-            e.printStackTrace();
+            logger.error("未处理的异常 ", e);
         }
         }
     }
     }
+
+    private Response getRegisterOkResponse(Request request) throws ParseException {
+        // 携带授权头并且密码正确
+        Response  response = getMessageFactory().createResponse(Response.OK, request);
+        // 添加date头
+        SIPDateHeader dateHeader = new SIPDateHeader();
+        // 使用自己修改的
+        WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
+        dateHeader.setDate(wvpSipDate);
+        response.addHeader(dateHeader);
+
+        // 添加Contact头
+        response.addHeader(request.getHeader(ContactHeader.NAME));
+        // 添加Expires头
+        response.addHeader(request.getExpires());
+
+        return response;
+
+    }
 }
 }

+ 7 - 7
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java

@@ -1,5 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
 
 
+import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.CmdType;
 import com.genersoft.iot.vmp.gb28181.bean.CmdType;
@@ -8,14 +9,11 @@ import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
 import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
 import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
-import gov.nist.javax.sip.SipProviderImpl;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPResponse;
 import gov.nist.javax.sip.message.SIPResponse;
 import org.dom4j.DocumentException;
 import org.dom4j.DocumentException;
@@ -26,7 +24,9 @@ import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 
 
-import javax.sip.*;
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
 import javax.sip.header.ExpiresHeader;
 import javax.sip.header.ExpiresHeader;
 import javax.sip.message.Response;
 import javax.sip.message.Response;
 import java.text.ParseException;
 import java.text.ParseException;
@@ -93,7 +93,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
 				sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
 				sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
 			}
 			}
 		} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
 		} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
-			e.printStackTrace();
+			logger.error("未处理的异常 ", e);
 		}
 		}
 
 
 	}
 	}
@@ -146,7 +146,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
 			}
 			}
 
 
 		} catch (SipException | InvalidArgumentException | ParseException e) {
 		} catch (SipException | InvalidArgumentException | ParseException e) {
-			e.printStackTrace();
+			logger.error("未处理的异常 ", e);
 		}
 		}
 	}
 	}
 
 
@@ -192,7 +192,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
 				subscribeHolder.putCatalogSubscribe(platformId, subscribeInfo);
 				subscribeHolder.putCatalogSubscribe(platformId, subscribeInfo);
 			}
 			}
 		} catch (SipException | InvalidArgumentException | ParseException e) {
 		} catch (SipException | InvalidArgumentException | ParseException e) {
-			e.printStackTrace();
+			logger.error("未处理的异常 ", e);
 		}
 		}
 	}
 	}
 }
 }

+ 32 - 29
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java

@@ -73,35 +73,38 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
         String channelId = getText(rootElement, "DeviceID");
         String channelId = getText(rootElement, "DeviceID");
         // 远程启动功能
         // 远程启动功能
         if (!ObjectUtils.isEmpty(getText(rootElement, "TeleBoot"))) {
         if (!ObjectUtils.isEmpty(getText(rootElement, "TeleBoot"))) {
-            if (parentPlatform.getServerGBId().equals(targetGBId)) {
-                // 远程启动本平台:需要在重新启动程序后先对SipStack解绑
-                logger.info("执行远程启动本平台命令");
-                try {
-                    cmderFroPlatform.unregister(parentPlatform, null, null);
-                } catch (InvalidArgumentException | ParseException | SipException e) {
-                    logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
-                }
-                taskExecutor.execute(() -> {
-                    // 远程启动
-//                    try {
-//                        Thread.sleep(3000);
-//                        SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
-//                        SipStackImpl stack = (SipStackImpl)up.getSipStack();
-//                        stack.stop();
-//                        Iterator listener = stack.getListeningPoints();
-//                        while (listener.hasNext()) {
-//                            stack.deleteListeningPoint((ListeningPoint) listener.next());
-//                        }
-//                        Iterator providers = stack.getSipProviders();
-//                        while (providers.hasNext()) {
-//                            stack.deleteSipProvider((SipProvider) providers.next());
-//                        }
-//                        VManageBootstrap.restart();
-//                    } catch (InterruptedException | ObjectInUseException e) {
-//                        logger.error("[任务执行失败] 服务重启: {}", e.getMessage());
-//                    }
-                });
-            }
+            // TODO 拒绝远程启动命令
+            logger.warn("[国标级联]收到平台的远程启动命令, 不处理");
+
+//            if (parentPlatform.getServerGBId().equals(targetGBId)) {
+//                // 远程启动本平台:需要在重新启动程序后先对SipStack解绑
+//                logger.info("执行远程启动本平台命令");
+//                try {
+//                    cmderFroPlatform.unregister(parentPlatform, null, null);
+//                } catch (InvalidArgumentException | ParseException | SipException e) {
+//                    logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
+//                }
+//                taskExecutor.execute(() -> {
+//                    // 远程启动
+////                    try {
+////                        Thread.sleep(3000);
+////                        SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
+////                        SipStackImpl stack = (SipStackImpl)up.getSipStack();
+////                        stack.stop();
+////                        Iterator listener = stack.getListeningPoints();
+////                        while (listener.hasNext()) {
+////                            stack.deleteListeningPoint((ListeningPoint) listener.next());
+////                        }
+////                        Iterator providers = stack.getSipProviders();
+////                        while (providers.hasNext()) {
+////                            stack.deleteSipProvider((SipProvider) providers.next());
+////                        }
+////                        VManageBootstrap.restart();
+////                    } catch (InterruptedException | ObjectInUseException e) {
+////                        logger.error("[任务执行失败] 服务重启: {}", e.getMessage());
+////                    }
+//                });
+//            }
         }
         }
         DeviceControlType deviceControlType = DeviceControlType.typeOf(rootElement);
         DeviceControlType deviceControlType = DeviceControlType.typeOf(rootElement);
         logger.info("[接受deviceControl命令] 命令: {}", deviceControlType);
         logger.info("[接受deviceControl命令] 命令: {}", deviceControlType);

+ 13 - 5
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java

@@ -186,9 +186,13 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
                             // 发送给平台的报警信息。 发送redis通知
                             // 发送给平台的报警信息。 发送redis通知
                             logger.info("[发送给平台的报警信息]内容:{}", JSONObject.toJSONString(deviceAlarm));
                             logger.info("[发送给平台的报警信息]内容:{}", JSONObject.toJSONString(deviceAlarm));
                             AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
                             AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
-                            alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
+                            if (deviceAlarm.getAlarmMethod() != null) {
+                                alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
+                            }
                             alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
                             alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
-                            alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
+                            if (deviceAlarm.getAlarmType() != null) {
+                                alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
+                            }
                             alarmChannelMessage.setGbId(channelId);
                             alarmChannelMessage.setGbId(channelId);
                             redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
                             redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
                             continue;
                             continue;
@@ -204,6 +208,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
                             publisher.deviceAlarmEventPublish(deviceAlarm);
                             publisher.deviceAlarmEventPublish(deviceAlarm);
                         }
                         }
                     }catch (Exception e) {
                     }catch (Exception e) {
+                        logger.error("未处理的异常 ", e);
                         logger.warn("[收到报警通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest());
                         logger.warn("[收到报警通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest());
                     }
                     }
                 }
                 }
@@ -264,12 +269,15 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
         if (channelId.equals(parentPlatform.getDeviceGBId())) {
         if (channelId.equals(parentPlatform.getDeviceGBId())) {
             // 发送给平台的报警信息。 发送redis通知
             // 发送给平台的报警信息。 发送redis通知
             AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
             AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
-            alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
+            if (deviceAlarm.getAlarmMethod() != null) {
+                alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
+            }
             alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
             alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
             alarmChannelMessage.setGbId(channelId);
             alarmChannelMessage.setGbId(channelId);
-            alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
+            if (deviceAlarm.getAlarmType() != null) {
+                alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
+            }
             redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
             redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
-            return;
         }
         }
     }
     }
 }
 }

+ 2 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java

@@ -88,13 +88,13 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
             // 对于已经离线的设备判断他的注册是否已经过期
             // 对于已经离线的设备判断他的注册是否已经过期
             if (!deviceService.expire(device)){
             if (!deviceService.expire(device)){
                 device.setOnline(0);
                 device.setOnline(0);
-                deviceService.online(device);
+                deviceService.online(device, null);
             }
             }
         }
         }
         // 刷新过期任务
         // 刷新过期任务
         String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
         String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
         // 如果三次心跳失败,则设置设备离线
         // 如果三次心跳失败,则设置设备离线
-        dynamicTask.startDelay(registerExpireTaskKey, ()-> deviceService.offline(device.getDeviceId()), device.getKeepaliveIntervalTime()*1000*3);
+        dynamicTask.startDelay(registerExpireTaskKey, ()-> deviceService.offline(device.getDeviceId(), "三次心跳失败"), device.getKeepaliveIntervalTime()*1000*3);
 
 
     }
     }
 
 

+ 3 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java

@@ -149,9 +149,10 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen
                         redisCatchStorage.sendMobilePositionMsg(jsonObject);
                         redisCatchStorage.sendMobilePositionMsg(jsonObject);
 
 
                     } catch (DocumentException e) {
                     } catch (DocumentException e) {
-                        e.printStackTrace();
+                        logger.error("未处理的异常 ", e);
                     } catch (Exception e) {
                     } catch (Exception e) {
-                        logger.warn("[移动位置通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest());
+                        logger.warn("[移动位置通知] 发现未处理的异常, \r\n{}", evt.getRequest());
+                        logger.error("[移动位置通知] 异常内容: ", e);
                     }
                     }
                 }
                 }
             });
             });

+ 12 - 5
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java

@@ -60,17 +60,24 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp
             return;
             return;
         }
         }
         String sn = rootElement.element("SN").getText();
         String sn = rootElement.element("SN").getText();
+
         /*根据WVP原有的数据结构,设备和通道是分开放置,设备信息都是存放在设备表里,通道表里的设备信息不可作为真实信息处理
         /*根据WVP原有的数据结构,设备和通道是分开放置,设备信息都是存放在设备表里,通道表里的设备信息不可作为真实信息处理
         大部分NVR/IPC设备对他的通道信息实现都是返回默认的值没有什么参考价值。NVR/IPC通道我们统一使用设备表的设备信息来作为返回。
         大部分NVR/IPC设备对他的通道信息实现都是返回默认的值没有什么参考价值。NVR/IPC通道我们统一使用设备表的设备信息来作为返回。
         我们这里使用查询数据库的方式来实现这个设备信息查询的功能,在其他地方对设备信息更新达到正确的目的。*/
         我们这里使用查询数据库的方式来实现这个设备信息查询的功能,在其他地方对设备信息更新达到正确的目的。*/
+
         String channelId = getText(rootElement, "DeviceID");
         String channelId = getText(rootElement, "DeviceID");
-        Device device = storager.queryDeviceInfoByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
-        if (device ==null){
-            logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+"  deviceID:"+channelId);
-            return;
+        // 查询这是通道id还是设备id
+        Device device = null;
+        // 如果id指向平台的国标编号,那么就是查询平台的信息
+        if (!parentPlatform.getDeviceGBId().equals(channelId)) {
+            device = storager.queryDeviceInfoByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
+            if (device ==null){
+                logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+"  deviceID:"+channelId);
+                return;
+            }
         }
         }
         try {
         try {
-            cmderFroPlatform.deviceInfoResponse(parentPlatform,device, sn, fromHeader.getTag());
+            cmderFroPlatform.deviceInfoResponse(parentPlatform, device, sn, fromHeader.getTag());
         } catch (SipException | InvalidArgumentException | ParseException e) {
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[命令发送失败] 国标级联 DeviceInfo查询回复: {}", e.getMessage());
             logger.error("[命令发送失败] 国标级联 DeviceInfo查询回复: {}", e.getMessage());
         }
         }

+ 2 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java

@@ -132,7 +132,8 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
 
 
                         }
                         }
                     }catch (Exception e) {
                     }catch (Exception e) {
-                        logger.warn("[收到通道] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest());
+                        logger.warn("[收到通道] 发现未处理的异常, \r\n{}", evt.getRequest());
+                        logger.error("[收到通道] 异常内容: ", e);
                     }
                     }
                 }
                 }
             });
             });

+ 2 - 5
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java

@@ -1,10 +1,8 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
 
 
 import com.alibaba.fastjson2.JSONObject;
 import com.alibaba.fastjson2.JSONObject;
-import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
-import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
@@ -26,7 +24,6 @@ import javax.sip.RequestEvent;
 import javax.sip.SipException;
 import javax.sip.SipException;
 import javax.sip.message.Response;
 import javax.sip.message.Response;
 import java.text.ParseException;
 import java.text.ParseException;
-import java.util.Objects;
 
 
 @Component
 @Component
 public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
 public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
@@ -74,9 +71,9 @@ public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParen
         }
         }
         String text = onlineElement.getText();
         String text = onlineElement.getText();
         if ("ONLINE".equalsIgnoreCase(text.trim())) {
         if ("ONLINE".equalsIgnoreCase(text.trim())) {
-            deviceService.online(device);
+            deviceService.online(device, null);
         }else {
         }else {
-            deviceService.offline(device.getDeviceId());
+            deviceService.offline(device.getDeviceId(), "设备状态查询结果:" + text.trim());
         }
         }
         RequestMessage msg = new RequestMessage();
         RequestMessage msg = new RequestMessage();
         msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId());
         msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId());

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java

@@ -142,7 +142,7 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar
             }
             }
 
 
         } catch (DocumentException e) {
         } catch (DocumentException e) {
-            e.printStackTrace();
+            logger.error("未处理的异常 ", e);
         }
         }
     }
     }
 
 

+ 7 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java

@@ -150,7 +150,8 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
                     }
                     }
                 }
                 }
             } catch (Exception e) {
             } catch (Exception e) {
-                logger.error("[国标录像] 发现未处理的异常, "+e.getMessage(), e);
+                logger.error("[国标录像] 发现未处理的异常, \r\n{}", evt.getRequest());
+                logger.error("[国标录像] 异常内容: ", e);
             }
             }
         });
         });
     }
     }
@@ -163,7 +164,11 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
     public void releaseRequest(String deviceId, String sn,RecordInfo recordInfo){
     public void releaseRequest(String deviceId, String sn,RecordInfo recordInfo){
         String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn;
         String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn;
         // 对数据进行排序
         // 对数据进行排序
-        Collections.sort(recordInfo.getRecordList());
+        if(recordInfo!=null && recordInfo.getRecordList()!=null) {
+            Collections.sort(recordInfo.getRecordList());
+        }else{
+            recordInfo.setRecordList(new ArrayList<>());
+        }
 
 
         RequestMessage msg = new RequestMessage();
         RequestMessage msg = new RequestMessage();
         msg.setKey(key);
         msg.setKey(key);

+ 8 - 6
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java

@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
 
 
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
+import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
 import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
 import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
@@ -10,6 +11,7 @@ import com.genersoft.iot.vmp.service.IPlatformService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
 import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
+import gov.nist.javax.sip.message.SIPResponse;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -18,7 +20,6 @@ import org.springframework.stereotype.Component;
 import javax.sip.InvalidArgumentException;
 import javax.sip.InvalidArgumentException;
 import javax.sip.ResponseEvent;
 import javax.sip.ResponseEvent;
 import javax.sip.SipException;
 import javax.sip.SipException;
-import javax.sip.header.CallIdHeader;
 import javax.sip.header.WWWAuthenticateHeader;
 import javax.sip.header.WWWAuthenticateHeader;
 import javax.sip.message.Response;
 import javax.sip.message.Response;
 import java.text.ParseException;
 import java.text.ParseException;
@@ -65,9 +66,8 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
 	 */
 	 */
 	@Override
 	@Override
 	public void process(ResponseEvent evt) {
 	public void process(ResponseEvent evt) {
-		Response response = evt.getResponse();
-		CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME);
-		String callId = callIdHeader.getCallId();
+		SIPResponse response = (SIPResponse)evt.getResponse();
+		String callId = response.getCallIdHeader().getCallId();
 		PlatformRegisterInfo platformRegisterInfo = redisCatchStorage.queryPlatformRegisterInfo(callId);
 		PlatformRegisterInfo platformRegisterInfo = redisCatchStorage.queryPlatformRegisterInfo(callId);
 		if (platformRegisterInfo == null) {
 		if (platformRegisterInfo == null) {
 			logger.info(String.format("[国标级联]未找到callId: %s 的注册/注销平台id", callId ));
 			logger.info(String.format("[国标级联]未找到callId: %s 的注册/注销平台id", callId ));
@@ -90,15 +90,17 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
 
 
 		if (response.getStatusCode() == Response.UNAUTHORIZED) {
 		if (response.getStatusCode() == Response.UNAUTHORIZED) {
 			WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME);
 			WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME);
+			SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response);
 			try {
 			try {
-				sipCommanderForPlatform.register(parentPlatform, callId, www, null, null, true, platformRegisterInfo.isRegister());
+				sipCommanderForPlatform.register(parentPlatform, sipTransactionInfo, www, null, null, true, platformRegisterInfo.isRegister());
 			} catch (SipException | InvalidArgumentException | ParseException e) {
 			} catch (SipException | InvalidArgumentException | ParseException e) {
 				logger.error("[命令发送失败] 国标级联 再次注册: {}", e.getMessage());
 				logger.error("[命令发送失败] 国标级联 再次注册: {}", e.getMessage());
 			}
 			}
 		}else if (response.getStatusCode() == Response.OK){
 		}else if (response.getStatusCode() == Response.OK){
 
 
 			if (platformRegisterInfo.isRegister()) {
 			if (platformRegisterInfo.isRegister()) {
-				platformService.online(parentPlatform);
+				SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response);
+				platformService.online(parentPlatform, sipTransactionInfo);
 			}else {
 			}else {
 				platformService.offline(parentPlatform, false);
 				platformService.offline(parentPlatform, false);
 			}
 			}

+ 5 - 5
src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java

@@ -9,17 +9,12 @@ import org.jetbrains.annotations.NotNull;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
-import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
 
 
-import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.IOException;
 import java.net.ConnectException;
 import java.net.ConnectException;
 import java.util.HashMap;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Objects;
-import java.util.concurrent.TimeUnit;
 
 
 @Component
 @Component
 public class AssistRESTfulUtils {
 public class AssistRESTfulUtils {
@@ -137,6 +132,11 @@ public class AssistRESTfulUtils {
         return sendGet(mediaServerItem, "api/record/file/duration",param, callback);
         return sendGet(mediaServerItem, "api/record/file/duration",param, callback);
     }
     }
 
 
+    public JSONObject getInfo(MediaServerItem mediaServerItem, RequestCallback callback){
+        Map<String, Object> param = new HashMap<>();
+        return sendGet(mediaServerItem, "api/record/info",param, callback);
+    }
+
     public JSONObject addStreamCallInfo(MediaServerItem mediaServerItem, String app, String stream, String callId, RequestCallback callback){
     public JSONObject addStreamCallInfo(MediaServerItem mediaServerItem, String app, String stream, String callId, RequestCallback callback){
         Map<String, Object> param = new HashMap<>();
         Map<String, Object> param = new HashMap<>();
         param.put("app",app);
         param.put("app",app);

+ 27 - 2
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java

@@ -119,10 +119,11 @@ public class ZLMHttpHookListener {
      * 服务器定时上报时间,上报间隔可配置,默认10s上报一次
      * 服务器定时上报时间,上报间隔可配置,默认10s上报一次
      */
      */
     @ResponseBody
     @ResponseBody
+
     @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")
     @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")
     public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) {
     public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) {
 
 
-        logger.info("[ZLM HOOK] 收到zlm心跳:" + param.getMediaServerId());
+//        logger.info("[ZLM HOOK] 收到zlm心跳:" + param.getMediaServerId());
 
 
         taskExecutor.execute(() -> {
         taskExecutor.execute(() -> {
             List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
             List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
@@ -142,6 +143,7 @@ public class ZLMHttpHookListener {
      * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。
      * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。
      */
      */
     @ResponseBody
     @ResponseBody
+
     @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
     @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
     public HookResult onPlay(@RequestBody OnPlayHookParam param) {
     public HookResult onPlay(@RequestBody OnPlayHookParam param) {
         if (logger.isDebugEnabled()) {
         if (logger.isDebugEnabled()) {
@@ -264,9 +266,28 @@ public class ZLMHttpHookListener {
             }
             }
 
 
         }
         }
+        if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
+            logger.info("推流时发现尚未设置录像路径,从assist服务中读取");
+            JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null);
+            if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) {
+                JSONObject dataJson = info.getJSONObject("data");
+                if (dataJson != null) {
+                    String recordPath = dataJson.getString("record");
+                    userSetting.setRecordPath(recordPath);
+                    result.setMp4_save_path(recordPath);
+                    // 修改zlm中的录像路径
+                    if (mediaInfo.isAutoConfig()) {
+                        taskExecutor.execute(() -> {
+                            mediaServerService.setZLMConfig(mediaInfo, false);
+                        });
+                    }
+                }
+            }
+        }
         return result;
         return result;
     }
     }
 
 
+
     /**
     /**
      * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
      * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
      */
      */
@@ -293,8 +314,12 @@ public class ZLMHttpHookListener {
                     subscribe.response(mediaInfo, json);
                     subscribe.response(mediaInfo, json);
                 }
                 }
             }
             }
-            // 流消失移除redis play
+
+            List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
+            // TODO 重构此处逻辑
+
             if (param.isRegist()) {
             if (param.isRegist()) {
+                // 处理流注册的鉴权信息
                 if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
                 if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
                         || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
                         || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
                         || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
                         || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {

+ 2 - 2
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java

@@ -334,9 +334,9 @@ public class ZLMRESTfulUtils {
         sendPost(mediaServerItem, "kick_sessions",param, null);
         sendPost(mediaServerItem, "kick_sessions",param, null);
     }
     }
 
 
-    public void getSnap(MediaServerItem mediaServerItem, String flvUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
+    public void getSnap(MediaServerItem mediaServerItem, String streamUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
         Map<String, Object> param = new HashMap<>(3);
         Map<String, Object> param = new HashMap<>(3);
-        param.put("url", flvUrl);
+        param.put("url", streamUrl);
         param.put("timeout_sec", timeout_sec);
         param.put("timeout_sec", timeout_sec);
         param.put("expire_sec", expire_sec);
         param.put("expire_sec", expire_sec);
         sendGetForImg(mediaServerItem, "getSnap", param, targetPath, fileName);
         sendGetForImg(mediaServerItem, "getSnap", param, targetPath, fileName);

+ 4 - 1
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java

@@ -262,8 +262,11 @@ public class ZLMRTPServerFactory {
                         logger.info("[保持端口] {}->监听端口到期继续保持监听", ssrc);
                         logger.info("[保持端口] {}->监听端口到期继续保持监听", ssrc);
                         keepPort(serverItem, ssrc);
                         keepPort(serverItem, ssrc);
                     });
                     });
-        }
         logger.info("[保持端口] {}->监听端口: {}", ssrc, localPort);
         logger.info("[保持端口] {}->监听端口: {}", ssrc, localPort);
+            logger.info("[保持端口] {}->监听端口: {}", ssrc, localPort);
+        }else {
+            logger.info("[保持端口] 监听端口失败: {}", ssrc);
+        }
         return localPort;
         return localPort;
     }
     }
 
 

+ 400 - 3
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java

@@ -10,21 +10,87 @@ public class ZLMServerConfig {
     @JSONField(name = "api.secret")
     @JSONField(name = "api.secret")
     private String apiSecret;
     private String apiSecret;
 
 
+    @JSONField(name = "api.snapRoot")
+    private String apiSnapRoot;
+
+    @JSONField(name = "api.defaultSnap")
+    private String apiDefaultSnap;
+
     @JSONField(name = "ffmpeg.bin")
     @JSONField(name = "ffmpeg.bin")
     private String ffmpegBin;
     private String ffmpegBin;
 
 
     @JSONField(name = "ffmpeg.cmd")
     @JSONField(name = "ffmpeg.cmd")
     private String ffmpegCmd;
     private String ffmpegCmd;
 
 
+    @JSONField(name = "ffmpeg.snap")
+    private String ffmpegSnap;
+
     @JSONField(name = "ffmpeg.log")
     @JSONField(name = "ffmpeg.log")
     private String ffmpegLog;
     private String ffmpegLog;
 
 
+    @JSONField(name = "ffmpeg.restart_sec")
+    private String ffmpegRestartSec;
+
+    @JSONField(name = "protocol.modify_stamp")
+    private String protocolModifyStamp;
+
+    @JSONField(name = "protocol.enable_audio")
+    private String protocolEnableAudio;
+
+    @JSONField(name = "protocol.add_mute_audio")
+    private String protocolAddMuteAudio;
+
+    @JSONField(name = "protocol.continue_push_ms")
+    private String protocolContinuePushMs;
+
+    @JSONField(name = "protocol.enable_hls")
+    private String protocolEnableHls;
+
+    @JSONField(name = "protocol.enable_mp4")
+    private String protocolEnableMp4;
+
+    @JSONField(name = "protocol.enable_rtsp")
+    private String protocolEnableRtsp;
+
+    @JSONField(name = "protocol.enable_rtmp")
+    private String protocolEnableRtmp;
+
+    @JSONField(name = "protocol.enable_ts")
+    private String protocolEnableTs;
+
+    @JSONField(name = "protocol.enable_fmp4")
+    private String protocolEnableFmp4;
+
+    @JSONField(name = "protocol.mp4_as_player")
+    private String protocolMp4AsPlayer;
+
+    @JSONField(name = "protocol.mp4_max_second")
+    private String protocolMp4MaxSecond;
+
+    @JSONField(name = "protocol.mp4_save_path")
+    private String protocolMp4SavePath;
+
+    @JSONField(name = "protocol.hls_save_path")
+    private String protocolHlsSavePath;
+
+    @JSONField(name = "protocol.hls_demand")
+    private String protocolHlsDemand;
+
+    @JSONField(name = "protocol.rtsp_demand")
+    private String protocolRtspDemand;
+
+    @JSONField(name = "protocol.rtmp_demand")
+    private String protocolRtmpDemand;
+
+    @JSONField(name = "protocol.ts_demand")
+    private String protocolTsDemand;
+
+    @JSONField(name = "protocol.fmp4_demand")
+    private String protocolFmp4Demand;
+
     @JSONField(name = "general.enableVhost")
     @JSONField(name = "general.enableVhost")
     private String generalEnableVhost;
     private String generalEnableVhost;
 
 
-    @JSONField(name = "general.mediaServerId")
-    private String generalMediaServerId;
-
     @JSONField(name = "general.flowThreshold")
     @JSONField(name = "general.flowThreshold")
     private String generalFlowThreshold;
     private String generalFlowThreshold;
 
 
@@ -34,6 +100,25 @@ public class ZLMServerConfig {
     @JSONField(name = "general.streamNoneReaderDelayMS")
     @JSONField(name = "general.streamNoneReaderDelayMS")
     private int generalStreamNoneReaderDelayMS;
     private int generalStreamNoneReaderDelayMS;
 
 
+    @JSONField(name = "general.resetWhenRePlay")
+    private String generalResetWhenRePlay;
+
+    @JSONField(name = "general.mergeWriteMS")
+    private String generalMergeWriteMS;
+
+    @JSONField(name = "general.mediaServerId")
+    private String generalMediaServerId;
+
+    @JSONField(name = "general.wait_track_ready_ms")
+    private String generalWaitTrackReadyMs;
+
+    @JSONField(name = "general.wait_add_track_ms")
+    private String generalWaitAddTrackMs;
+
+    @JSONField(name = "general.unready_frame_cache")
+    private String generalUnreadyFrameCache;
+
+
     @JSONField(name = "ip")
     @JSONField(name = "ip")
     private String ip;
     private String ip;
 
 
@@ -59,6 +144,18 @@ public class ZLMServerConfig {
     @JSONField(name = "hls.segNum")
     @JSONField(name = "hls.segNum")
     private String hlsSegNum;
     private String hlsSegNum;
 
 
+    @JSONField(name = "hls.segRetain")
+    private String hlsSegRetain;
+
+    @JSONField(name = "hls.broadcastRecordTs")
+    private String hlsBroadcastRecordTs;
+
+    @JSONField(name = "hls.deleteDelaySec")
+    private String hlsDeleteDelaySec;
+
+    @JSONField(name = "hls.segKeep")
+    private String hlsSegKeep;
+
     @JSONField(name = "hook.access_file_except_hls")
     @JSONField(name = "hook.access_file_except_hls")
     private String hookAccessFileExceptHLS;
     private String hookAccessFileExceptHLS;
 
 
@@ -104,6 +201,18 @@ public class ZLMServerConfig {
     @JSONField(name = "hook.on_stream_not_found")
     @JSONField(name = "hook.on_stream_not_found")
     private String hookOnStreamNotFound;
     private String hookOnStreamNotFound;
 
 
+    @JSONField(name = "hook.on_server_started")
+    private String hookOnServerStarted;
+
+    @JSONField(name = "hook.on_server_keepalive")
+    private String hookOnServerKeepalive;
+
+    @JSONField(name = "hook.on_send_rtp_stopped")
+    private String hookOnSendRtpStopped;
+
+    @JSONField(name = "hook.on_rtp_server_timeout")
+    private String hookOnRtpServerTimeout;
+
     @JSONField(name = "hook.timeoutSec")
     @JSONField(name = "hook.timeoutSec")
     private String hookTimeoutSec;
     private String hookTimeoutSec;
 
 
@@ -813,4 +922,292 @@ public class ZLMServerConfig {
     public void setPortRange(String portRange) {
     public void setPortRange(String portRange) {
         this.portRange = portRange;
         this.portRange = portRange;
     }
     }
+
+    public String getApiSnapRoot() {
+        return apiSnapRoot;
+    }
+
+    public void setApiSnapRoot(String apiSnapRoot) {
+        this.apiSnapRoot = apiSnapRoot;
+    }
+
+    public String getApiDefaultSnap() {
+        return apiDefaultSnap;
+    }
+
+    public void setApiDefaultSnap(String apiDefaultSnap) {
+        this.apiDefaultSnap = apiDefaultSnap;
+    }
+
+    public String getFfmpegSnap() {
+        return ffmpegSnap;
+    }
+
+    public void setFfmpegSnap(String ffmpegSnap) {
+        this.ffmpegSnap = ffmpegSnap;
+    }
+
+    public String getFfmpegRestartSec() {
+        return ffmpegRestartSec;
+    }
+
+    public void setFfmpegRestartSec(String ffmpegRestartSec) {
+        this.ffmpegRestartSec = ffmpegRestartSec;
+    }
+
+    public String getProtocolModifyStamp() {
+        return protocolModifyStamp;
+    }
+
+    public void setProtocolModifyStamp(String protocolModifyStamp) {
+        this.protocolModifyStamp = protocolModifyStamp;
+    }
+
+    public String getProtocolEnableAudio() {
+        return protocolEnableAudio;
+    }
+
+    public void setProtocolEnableAudio(String protocolEnableAudio) {
+        this.protocolEnableAudio = protocolEnableAudio;
+    }
+
+    public String getProtocolAddMuteAudio() {
+        return protocolAddMuteAudio;
+    }
+
+    public void setProtocolAddMuteAudio(String protocolAddMuteAudio) {
+        this.protocolAddMuteAudio = protocolAddMuteAudio;
+    }
+
+    public String getProtocolContinuePushMs() {
+        return protocolContinuePushMs;
+    }
+
+    public void setProtocolContinuePushMs(String protocolContinuePushMs) {
+        this.protocolContinuePushMs = protocolContinuePushMs;
+    }
+
+    public String getProtocolEnableHls() {
+        return protocolEnableHls;
+    }
+
+    public void setProtocolEnableHls(String protocolEnableHls) {
+        this.protocolEnableHls = protocolEnableHls;
+    }
+
+    public String getProtocolEnableMp4() {
+        return protocolEnableMp4;
+    }
+
+    public void setProtocolEnableMp4(String protocolEnableMp4) {
+        this.protocolEnableMp4 = protocolEnableMp4;
+    }
+
+    public String getProtocolEnableRtsp() {
+        return protocolEnableRtsp;
+    }
+
+    public void setProtocolEnableRtsp(String protocolEnableRtsp) {
+        this.protocolEnableRtsp = protocolEnableRtsp;
+    }
+
+    public String getProtocolEnableRtmp() {
+        return protocolEnableRtmp;
+    }
+
+    public void setProtocolEnableRtmp(String protocolEnableRtmp) {
+        this.protocolEnableRtmp = protocolEnableRtmp;
+    }
+
+    public String getProtocolEnableTs() {
+        return protocolEnableTs;
+    }
+
+    public void setProtocolEnableTs(String protocolEnableTs) {
+        this.protocolEnableTs = protocolEnableTs;
+    }
+
+    public String getProtocolEnableFmp4() {
+        return protocolEnableFmp4;
+    }
+
+    public void setProtocolEnableFmp4(String protocolEnableFmp4) {
+        this.protocolEnableFmp4 = protocolEnableFmp4;
+    }
+
+    public String getProtocolMp4AsPlayer() {
+        return protocolMp4AsPlayer;
+    }
+
+    public void setProtocolMp4AsPlayer(String protocolMp4AsPlayer) {
+        this.protocolMp4AsPlayer = protocolMp4AsPlayer;
+    }
+
+    public String getProtocolMp4MaxSecond() {
+        return protocolMp4MaxSecond;
+    }
+
+    public void setProtocolMp4MaxSecond(String protocolMp4MaxSecond) {
+        this.protocolMp4MaxSecond = protocolMp4MaxSecond;
+    }
+
+    public String getProtocolMp4SavePath() {
+        return protocolMp4SavePath;
+    }
+
+    public void setProtocolMp4SavePath(String protocolMp4SavePath) {
+        this.protocolMp4SavePath = protocolMp4SavePath;
+    }
+
+    public String getProtocolHlsSavePath() {
+        return protocolHlsSavePath;
+    }
+
+    public void setProtocolHlsSavePath(String protocolHlsSavePath) {
+        this.protocolHlsSavePath = protocolHlsSavePath;
+    }
+
+    public String getProtocolHlsDemand() {
+        return protocolHlsDemand;
+    }
+
+    public void setProtocolHlsDemand(String protocolHlsDemand) {
+        this.protocolHlsDemand = protocolHlsDemand;
+    }
+
+    public String getProtocolRtspDemand() {
+        return protocolRtspDemand;
+    }
+
+    public void setProtocolRtspDemand(String protocolRtspDemand) {
+        this.protocolRtspDemand = protocolRtspDemand;
+    }
+
+    public String getProtocolRtmpDemand() {
+        return protocolRtmpDemand;
+    }
+
+    public void setProtocolRtmpDemand(String protocolRtmpDemand) {
+        this.protocolRtmpDemand = protocolRtmpDemand;
+    }
+
+    public String getProtocolTsDemand() {
+        return protocolTsDemand;
+    }
+
+    public void setProtocolTsDemand(String protocolTsDemand) {
+        this.protocolTsDemand = protocolTsDemand;
+    }
+
+    public String getProtocolFmp4Demand() {
+        return protocolFmp4Demand;
+    }
+
+    public void setProtocolFmp4Demand(String protocolFmp4Demand) {
+        this.protocolFmp4Demand = protocolFmp4Demand;
+    }
+
+    public String getGeneralResetWhenRePlay() {
+        return generalResetWhenRePlay;
+    }
+
+    public void setGeneralResetWhenRePlay(String generalResetWhenRePlay) {
+        this.generalResetWhenRePlay = generalResetWhenRePlay;
+    }
+
+    public String getGeneralMergeWriteMS() {
+        return generalMergeWriteMS;
+    }
+
+    public void setGeneralMergeWriteMS(String generalMergeWriteMS) {
+        this.generalMergeWriteMS = generalMergeWriteMS;
+    }
+
+    public String getGeneralWaitTrackReadyMs() {
+        return generalWaitTrackReadyMs;
+    }
+
+    public void setGeneralWaitTrackReadyMs(String generalWaitTrackReadyMs) {
+        this.generalWaitTrackReadyMs = generalWaitTrackReadyMs;
+    }
+
+    public String getGeneralWaitAddTrackMs() {
+        return generalWaitAddTrackMs;
+    }
+
+    public void setGeneralWaitAddTrackMs(String generalWaitAddTrackMs) {
+        this.generalWaitAddTrackMs = generalWaitAddTrackMs;
+    }
+
+    public String getGeneralUnreadyFrameCache() {
+        return generalUnreadyFrameCache;
+    }
+
+    public void setGeneralUnreadyFrameCache(String generalUnreadyFrameCache) {
+        this.generalUnreadyFrameCache = generalUnreadyFrameCache;
+    }
+
+    public String getHlsSegRetain() {
+        return hlsSegRetain;
+    }
+
+    public void setHlsSegRetain(String hlsSegRetain) {
+        this.hlsSegRetain = hlsSegRetain;
+    }
+
+    public String getHlsBroadcastRecordTs() {
+        return hlsBroadcastRecordTs;
+    }
+
+    public void setHlsBroadcastRecordTs(String hlsBroadcastRecordTs) {
+        this.hlsBroadcastRecordTs = hlsBroadcastRecordTs;
+    }
+
+    public String getHlsDeleteDelaySec() {
+        return hlsDeleteDelaySec;
+    }
+
+    public void setHlsDeleteDelaySec(String hlsDeleteDelaySec) {
+        this.hlsDeleteDelaySec = hlsDeleteDelaySec;
+    }
+
+    public String getHlsSegKeep() {
+        return hlsSegKeep;
+    }
+
+    public void setHlsSegKeep(String hlsSegKeep) {
+        this.hlsSegKeep = hlsSegKeep;
+    }
+
+    public String getHookOnServerStarted() {
+        return hookOnServerStarted;
+    }
+
+    public void setHookOnServerStarted(String hookOnServerStarted) {
+        this.hookOnServerStarted = hookOnServerStarted;
+    }
+
+    public String getHookOnServerKeepalive() {
+        return hookOnServerKeepalive;
+    }
+
+    public void setHookOnServerKeepalive(String hookOnServerKeepalive) {
+        this.hookOnServerKeepalive = hookOnServerKeepalive;
+    }
+
+    public String getHookOnSendRtpStopped() {
+        return hookOnSendRtpStopped;
+    }
+
+    public void setHookOnSendRtpStopped(String hookOnSendRtpStopped) {
+        this.hookOnSendRtpStopped = hookOnSendRtpStopped;
+    }
+
+    public String getHookOnRtpServerTimeout() {
+        return hookOnRtpServerTimeout;
+    }
+
+    public void setHookOnRtpServerTimeout(String hookOnRtpServerTimeout) {
+        this.hookOnRtpServerTimeout = hookOnRtpServerTimeout;
+    }
 }
 }

+ 9 - 0
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java

@@ -5,6 +5,7 @@ public class HookResultForOnPublish extends HookResult{
     private boolean enable_audio;
     private boolean enable_audio;
     private boolean enable_mp4;
     private boolean enable_mp4;
     private int mp4_max_second;
     private int mp4_max_second;
+    private String mp4_save_path;
 
 
     public HookResultForOnPublish() {
     public HookResultForOnPublish() {
     }
     }
@@ -41,4 +42,12 @@ public class HookResultForOnPublish extends HookResult{
     public void setMp4_max_second(int mp4_max_second) {
     public void setMp4_max_second(int mp4_max_second) {
         this.mp4_max_second = mp4_max_second;
         this.mp4_max_second = mp4_max_second;
     }
     }
+
+    public String getMp4_save_path() {
+        return mp4_save_path;
+    }
+
+    public void setMp4_save_path(String mp4_save_path) {
+        this.mp4_save_path = mp4_save_path;
+    }
 }
 }

+ 3 - 2
src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java

@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.service;
 
 
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
 import com.genersoft.iot.vmp.gb28181.bean.SyncStatus;
 import com.genersoft.iot.vmp.gb28181.bean.SyncStatus;
 import com.genersoft.iot.vmp.vmanager.bean.BaseTree;
 import com.genersoft.iot.vmp.vmanager.bean.BaseTree;
 import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
 import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
@@ -18,13 +19,13 @@ public interface IDeviceService {
      * 设备上线
      * 设备上线
      * @param device 设备信息
      * @param device 设备信息
      */
      */
-    void online(Device device);
+    void online(Device device, SipTransactionInfo sipTransactionInfo);
 
 
     /**
     /**
      * 设备下线
      * 设备下线
      * @param deviceId 设备编号
      * @param deviceId 设备编号
      */
      */
-    void offline(String deviceId);
+    void offline(String deviceId, String reason);
 
 
     /**
     /**
      * 添加目录订阅
      * 添加目录订阅

+ 8 - 1
src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java

@@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
 import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
+import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
 import com.github.pagehelper.PageInfo;
 import com.github.pagehelper.PageInfo;
 
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.InvalidArgumentException;
@@ -34,11 +35,17 @@ public interface IPlatformService {
      */
      */
     boolean add(ParentPlatform parentPlatform);
     boolean add(ParentPlatform parentPlatform);
 
 
+    /**
+     * 添加级联平台
+     * @param parentPlatform 级联平台
+     */
+    boolean update(ParentPlatform parentPlatform);
+
     /**
     /**
      * 平台上线
      * 平台上线
      * @param parentPlatform 平台信息
      * @param parentPlatform 平台信息
      */
      */
-    void online(ParentPlatform parentPlatform);
+    void online(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo);
 
 
     /**
     /**
      * 平台离线
      * 平台离线

+ 12 - 4
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java

@@ -96,7 +96,7 @@ public class DeviceServiceImpl implements IDeviceService {
     private ZLMRESTfulUtils zlmresTfulUtils;
     private ZLMRESTfulUtils zlmresTfulUtils;
 
 
     @Override
     @Override
-    public void online(Device device) {
+    public void online(Device device, SipTransactionInfo sipTransactionInfo) {
         logger.info("[设备上线] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort());
         logger.info("[设备上线] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort());
         Device deviceInRedis = redisCatchStorage.getDevice(device.getDeviceId());
         Device deviceInRedis = redisCatchStorage.getDevice(device.getDeviceId());
         Device deviceInDb = deviceMapper.getDeviceByDeviceId(device.getDeviceId());
         Device deviceInDb = deviceMapper.getDeviceByDeviceId(device.getDeviceId());
@@ -111,6 +111,14 @@ public class DeviceServiceImpl implements IDeviceService {
             // 默认心跳间隔60
             // 默认心跳间隔60
             device.setKeepaliveIntervalTime(60);
             device.setKeepaliveIntervalTime(60);
         }
         }
+        if (sipTransactionInfo != null) {
+            device.setSipTransactionInfo(sipTransactionInfo);
+        }else {
+            if (deviceInRedis != null) {
+                device.setSipTransactionInfo(deviceInRedis.getSipTransactionInfo());
+            }
+        }
+
         // 第一次上线 或则设备之前是离线状态--进行通道同步和设备信息查询
         // 第一次上线 或则设备之前是离线状态--进行通道同步和设备信息查询
         if (device.getCreateTime() == null) {
         if (device.getCreateTime() == null) {
             device.setOnline(1);
             device.setOnline(1);
@@ -163,12 +171,12 @@ public class DeviceServiceImpl implements IDeviceService {
         // 刷新过期任务
         // 刷新过期任务
         String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
         String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
         // 如果第一次注册那么必须在60 * 3时间内收到一个心跳,否则设备离线
         // 如果第一次注册那么必须在60 * 3时间内收到一个心跳,否则设备离线
-        dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId()), device.getKeepaliveIntervalTime() * 1000 * 3);
+        dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId(), "首次注册后未能收到心跳"), device.getKeepaliveIntervalTime() * 1000 * 3);
     }
     }
 
 
     @Override
     @Override
-    public void offline(String deviceId) {
-        logger.error("[设备离线], device:{}", deviceId);
+    public void offline(String deviceId, String reason) {
+        logger.error("[设备离线],{}, device:{}", reason, deviceId);
         Device device = deviceMapper.getDeviceByDeviceId(deviceId);
         Device device = deviceMapper.getDeviceByDeviceId(deviceId);
         if (device == null) {
         if (device == null) {
             return;
             return;

+ 12 - 2
src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java

@@ -40,6 +40,9 @@ public class GbStreamServiceImpl implements IGbStreamService {
     @Autowired
     @Autowired
     private PlatformGbStreamMapper platformGbStreamMapper;
     private PlatformGbStreamMapper platformGbStreamMapper;
 
 
+    @Autowired
+    private SubscribeHolder subscribeHolder;
+
     @Autowired
     @Autowired
     private ParentPlatformMapper platformMapper;
     private ParentPlatformMapper platformMapper;
 
 
@@ -73,16 +76,23 @@ public class GbStreamServiceImpl implements IGbStreamService {
         }
         }
         try {
         try {
             List<DeviceChannel> deviceChannelList = new ArrayList<>();
             List<DeviceChannel> deviceChannelList = new ArrayList<>();
-            for (GbStream gbStream : gbStreams) {
+
+
+            for (int i = 0; i < gbStreams.size(); i++) {
+                GbStream gbStream = gbStreams.get(i);
                 gbStream.setCatalogId(catalogId);
                 gbStream.setCatalogId(catalogId);
                 gbStream.setPlatformId(platformId);
                 gbStream.setPlatformId(platformId);
                 // TODO 修改为批量提交
                 // TODO 修改为批量提交
                 platformGbStreamMapper.add(gbStream);
                 platformGbStreamMapper.add(gbStream);
+                logger.info("[关联通道]直播流通道 平台:{}, 共需关联通道数:{}, 已关联:{}", platformId, gbStreams.size(), i + 1);
                 DeviceChannel deviceChannelListByStream = getDeviceChannelListByStreamWithStatus(gbStream, catalogId, parentPlatform);
                 DeviceChannel deviceChannelListByStream = getDeviceChannelListByStreamWithStatus(gbStream, catalogId, parentPlatform);
                 deviceChannelList.add(deviceChannelListByStream);
                 deviceChannelList.add(deviceChannelListByStream);
             }
             }
             dataSourceTransactionManager.commit(transactionStatus);     //手动提交
             dataSourceTransactionManager.commit(transactionStatus);     //手动提交
-            eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
+            if (subscribeHolder.getCatalogSubscribe(platformId) != null) {
+                eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
+            }
+
             result = true;
             result = true;
         }catch (Exception e) {
         }catch (Exception e) {
             logger.error("批量保存流与平台的关系时错误", e);
             logger.error("批量保存流与平台的关系时错误", e);

+ 29 - 0
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java

@@ -11,6 +11,7 @@ import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
 import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
+import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
@@ -38,6 +39,7 @@ import org.springframework.transaction.TransactionDefinition;
 import org.springframework.transaction.TransactionStatus;
 import org.springframework.transaction.TransactionStatus;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.ObjectUtils;
 
 
+import java.io.File;
 import java.time.LocalDateTime;
 import java.time.LocalDateTime;
 import java.util.*;
 import java.util.*;
 
 
@@ -63,6 +65,9 @@ public class MediaServerServiceImpl implements IMediaServerService {
     @Autowired
     @Autowired
     private UserSetting userSetting;
     private UserSetting userSetting;
 
 
+    @Autowired
+    private AssistRESTfulUtils assistRESTfulUtils;
+
     @Autowired
     @Autowired
     private ZLMRESTfulUtils zlmresTfulUtils;
     private ZLMRESTfulUtils zlmresTfulUtils;
 
 
@@ -409,13 +414,27 @@ public class MediaServerServiceImpl implements IMediaServerService {
         }
         }
         RedisUtil.set(key, serverItem);
         RedisUtil.set(key, serverItem);
         resetOnlineServerItem(serverItem);
         resetOnlineServerItem(serverItem);
+
+
         if (serverItem.isAutoConfig()) {
         if (serverItem.isAutoConfig()) {
+            // 查看assist服务的录像路径配置
+            if (serverItem.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
+                JSONObject info = assistRESTfulUtils.getInfo(serverItem, null);
+                if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) {
+                    JSONObject dataJson = info.getJSONObject("data");
+                    if (dataJson != null) {
+                        String recordPath = dataJson.getString("record");
+                        userSetting.setRecordPath(recordPath);
+                    }
+                }
+            }
             setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable()));
             setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable()));
         }
         }
         final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId();
         final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId();
         dynamicTask.stop(zlmKeepaliveKey);
         dynamicTask.stop(zlmKeepaliveKey);
         dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(serverItem), (Math.getExponent(serverItem.getHookAliveInterval()) + 5) * 1000);
         dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(serverItem), (Math.getExponent(serverItem.getHookAliveInterval()) + 5) * 1000);
         publisher.zlmOnlineEventPublish(serverItem.getId());
         publisher.zlmOnlineEventPublish(serverItem.getId());
+
         logger.info("[ZLM] 连接成功 {} - {}:{} ",
         logger.info("[ZLM] 连接成功 {} - {}:{} ",
                 zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
                 zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
     }
     }
@@ -549,6 +568,9 @@ public class MediaServerServiceImpl implements IMediaServerService {
 
 
         Map<String, Object> param = new HashMap<>();
         Map<String, Object> param = new HashMap<>();
         param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
         param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
+        if (mediaServerItem.getRtspPort() != 0) {
+            param.put("ffmpeg.snap", "%s -rtsp_transport tcp -i %s -y -f mjpeg -t 0.001 %s");
+        }
         param.put("hook.enable","1");
         param.put("hook.enable","1");
         param.put("hook.on_flow_report","");
         param.put("hook.on_flow_report","");
         param.put("hook.on_play",String.format("%s/on_play", hookPrex));
         param.put("hook.on_play",String.format("%s/on_play", hookPrex));
@@ -583,6 +605,13 @@ public class MediaServerServiceImpl implements IMediaServerService {
             param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-"));
             param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-"));
         }
         }
 
 
+        if (userSetting.getRecordPath() != null) {
+            File recordPathFile = new File(userSetting.getRecordPath());
+            File mp4SavePathFile = recordPathFile.getParentFile().getAbsoluteFile();
+            param.put("protocol.mp4_save_path", mp4SavePathFile.getAbsoluteFile());
+            param.put("record.appName", recordPathFile.getName());
+        }
+
         JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaServerItem, param);
         JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaServerItem, param);
 
 
         if (responseJSON != null && responseJSON.getInteger("code") == 0) {
         if (responseJSON != null && responseJSON.getInteger("code") == 0) {

+ 52 - 12
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java

@@ -1,9 +1,6 @@
 package com.genersoft.iot.vmp.service.impl;
 package com.genersoft.iot.vmp.service.impl;
 
 
-import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
-import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
-import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
-import com.genersoft.iot.vmp.gb28181.bean.TreeType;
+import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
 import com.genersoft.iot.vmp.service.IPlatformChannelService;
 import com.genersoft.iot.vmp.service.IPlatformChannelService;
@@ -15,7 +12,10 @@ import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.TransactionStatus;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.ObjectUtils;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -34,6 +34,16 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService {
     @Autowired
     @Autowired
     private PlatformChannelMapper platformChannelMapper;
     private PlatformChannelMapper platformChannelMapper;
 
 
+    @Autowired
+    TransactionDefinition transactionDefinition;
+
+    @Autowired
+    DataSourceTransactionManager dataSourceTransactionManager;
+
+    @Autowired
+    private SubscribeHolder subscribeHolder;
+
+
     @Autowired
     @Autowired
     private DeviceChannelMapper deviceChannelMapper;
     private DeviceChannelMapper deviceChannelMapper;
 
 
@@ -69,17 +79,47 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService {
         }
         }
         List<ChannelReduce> channelReducesToAdd = new ArrayList<>(deviceAndChannels.values());
         List<ChannelReduce> channelReducesToAdd = new ArrayList<>(deviceAndChannels.values());
         // 对剩下的数据进行存储
         // 对剩下的数据进行存储
-        int result = 0;
+        int allCount = 0;
+        boolean result = false;
+        TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
+        int limitCount = 300;
         if (channelReducesToAdd.size() > 0) {
         if (channelReducesToAdd.size() > 0) {
-            result = platformChannelMapper.addChannels(platformId, channelReducesToAdd);
-            // TODO 后续给平台增加控制开关以控制是否响应目录订阅
-            List<DeviceChannel> deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId, platform);
-            if (deviceChannelList != null) {
-                eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
+            if (channelReducesToAdd.size() > limitCount) {
+                for (int i = 0; i < channelReducesToAdd.size(); i += limitCount) {
+                    int toIndex = i + limitCount;
+                    if (i + limitCount > channelReducesToAdd.size()) {
+                        toIndex = channelReducesToAdd.size();
+                    }
+                    int count = platformChannelMapper.addChannels(platformId, channelReducesToAdd.subList(i, toIndex));
+                    result = result || count < 0;
+                    allCount += count;
+                    logger.info("[关联通道]国标通道 平台:{}, 共需关联通道数:{}, 已关联:{}", platformId, channelReducesToAdd.size(), toIndex);
+                }
+            }else {
+                allCount = platformChannelMapper.addChannels(platformId, channelReducesToAdd);
+                result = result || allCount < 0;
+                logger.info("[关联通道]国标通道 平台:{}, 关联通道数:{}", platformId, channelReducesToAdd.size());
             }
             }
-        }
 
 
-        return result;
+            if (result) {
+                //事务回滚
+                dataSourceTransactionManager.rollback(transactionStatus);
+                allCount = 0;
+            }else {
+                logger.info("[关联通道]国标通道 平台:{}, 正在存入数据库", platformId);
+                dataSourceTransactionManager.commit(transactionStatus);
+
+            }
+            SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(platformId);
+            if (catalogSubscribe != null) {
+                List<DeviceChannel> deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId, platform);
+                if (deviceChannelList != null) {
+                    eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
+                }
+            }
+            logger.info("[关联通道]国标通道 平台:{}, 存入数据库成功", platformId);
+        }
+        return allCount;
     }
     }
 
 
     private List<DeviceChannel> getDeviceChannelListByChannelReduceList(List<ChannelReduce> channelReduces, String catalogId, ParentPlatform platform) {
     private List<DeviceChannel> getDeviceChannelListByChannelReduceList(List<ChannelReduce> channelReduces, String catalogId, ParentPlatform platform) {

+ 97 - 18
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java

@@ -21,8 +21,8 @@ import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
 import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
 import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
-import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper;
+import com.genersoft.iot.vmp.storager.dao.*;
+import com.genersoft.iot.vmp.utils.DateUtil;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import com.github.pagehelper.PageInfo;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
@@ -53,6 +53,15 @@ public class PlatformServiceImpl implements IPlatformService {
     @Autowired
     @Autowired
     private ParentPlatformMapper platformMapper;
     private ParentPlatformMapper platformMapper;
 
 
+    @Autowired
+    private PlatformCatalogMapper catalogMapper;
+
+    @Autowired
+    private PlatformChannelMapper platformChannelMapper;
+
+    @Autowired
+    private PlatformGbStreamMapper platformGbStreamMapper;
+
     @Autowired
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
     private IRedisCatchStorage redisCatchStorage;
 
 
@@ -135,36 +144,106 @@ public class PlatformServiceImpl implements IPlatformService {
     }
     }
 
 
     @Override
     @Override
-    public void online(ParentPlatform parentPlatform) {
-        logger.info("[国标级联]:{}, 平台上线/更新注册", parentPlatform.getServerGBId());
+    public boolean update(ParentPlatform parentPlatform) {
+        logger.info("[国标级联]更新平台 {}", parentPlatform.getDeviceGBId());
+        parentPlatform.setCharacterSet(parentPlatform.getCharacterSet().toUpperCase());
+        ParentPlatform parentPlatformOld = platformMapper.getParentPlatById(parentPlatform.getId());
+        ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatformOld.getServerGBId());
+        parentPlatform.setUpdateTime(DateUtil.getNow());
+        if (!parentPlatformOld.getTreeType().equals(parentPlatform.getTreeType())) {
+            // 目录结构发生变化,清空之前的关联关系
+            logger.info("保存平台{}时发现目录结构变化,清空关联关系", parentPlatform.getDeviceGBId());
+            catalogMapper.delByPlatformId(parentPlatformOld.getServerGBId());
+            platformChannelMapper.delByPlatformId(parentPlatformOld.getServerGBId());
+            platformGbStreamMapper.delByPlatformId(parentPlatformOld.getServerGBId());
+        }
+
+
+        // 停止心跳定时
+        final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatformOld.getServerGBId();
+        dynamicTask.stop(keepaliveTaskKey);
+        // 停止注册定时
+        final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatformOld.getServerGBId();
+        dynamicTask.stop(registerTaskKey);
+        // 注销旧的
+        try {
+            if (parentPlatformOld.isStatus()) {
+                logger.info("保存平台{}时发现救平台在线,发送注销命令", parentPlatform.getDeviceGBId());
+                commanderForPlatform.unregister(parentPlatformOld, parentPlatformCatchOld.getSipTransactionInfo(), null, eventResult -> {
+                    logger.info("[国标级联] 注销成功, 平台:{}", parentPlatformOld.getServerGBId());
+                });
+            }
+
+        } catch (InvalidArgumentException | ParseException | SipException e) {
+            logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
+        }
+
+        // 更新数据库
+        if (parentPlatform.getCatalogGroup() == 0) {
+            parentPlatform.setCatalogGroup(1);
+        }
+        if (parentPlatform.getAdministrativeDivision() == null) {
+            parentPlatform.setAdministrativeDivision(parentPlatform.getAdministrativeDivision());
+        }
+
+        platformMapper.updateParentPlatform(parentPlatform);
+        // 更新redis
+        redisCatchStorage.delPlatformCatchInfo(parentPlatformOld.getServerGBId());
+        ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch();
+        parentPlatformCatch.setParentPlatform(parentPlatform);
+        parentPlatformCatch.setId(parentPlatform.getServerGBId());
+        redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
+        // 注册
+        if (parentPlatform.isEnable()) {
+            // 保存时启用就发送注册
+            // 注册成功时由程序直接调用了online方法
+            try {
+                commanderForPlatform.register(parentPlatform, eventResult -> {
+                    logger.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", parentPlatform.getServerGBId());
+                }, null);
+            } catch (InvalidArgumentException | ParseException | SipException e) {
+                logger.error("[命令发送失败] 国标级联: {}", e.getMessage());
+            }
+        }
+        // 重新开启定时注册, 使用续订消息
+        // 重新开始心跳保活
+
+
+        return false;
+    }
+
+
+    @Override
+    public void online(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo) {
+        logger.info("[国标级联]:{}, 平台上线", parentPlatform.getServerGBId());
         platformMapper.updateParentPlatformStatus(parentPlatform.getServerGBId(), true);
         platformMapper.updateParentPlatformStatus(parentPlatform.getServerGBId(), true);
         ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
         ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
-        if (parentPlatformCatch != null) {
-            parentPlatformCatch.getParentPlatform().setStatus(true);
-            redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
-        }else {
+        if (parentPlatformCatch == null) {
             parentPlatformCatch = new ParentPlatformCatch();
             parentPlatformCatch = new ParentPlatformCatch();
             parentPlatformCatch.setParentPlatform(parentPlatform);
             parentPlatformCatch.setParentPlatform(parentPlatform);
             parentPlatformCatch.setId(parentPlatform.getServerGBId());
             parentPlatformCatch.setId(parentPlatform.getServerGBId());
             parentPlatform.setStatus(true);
             parentPlatform.setStatus(true);
             parentPlatformCatch.setParentPlatform(parentPlatform);
             parentPlatformCatch.setParentPlatform(parentPlatform);
-            redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
         }
         }
 
 
+        parentPlatformCatch.getParentPlatform().setStatus(true);
+        parentPlatformCatch.setSipTransactionInfo(sipTransactionInfo);
+        redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
+
         final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId();
         final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId();
         if (!dynamicTask.isAlive(registerTaskKey)) {
         if (!dynamicTask.isAlive(registerTaskKey)) {
+            logger.info("[国标级联]:{}, 添加定时注册任务", parentPlatform.getServerGBId());
             // 添加注册任务
             // 添加注册任务
             dynamicTask.startCron(registerTaskKey,
             dynamicTask.startCron(registerTaskKey,
                 // 注册失败(注册成功时由程序直接调用了online方法)
                 // 注册失败(注册成功时由程序直接调用了online方法)
-                ()-> {
-                    registerTask(parentPlatform);
-                },
-                (parentPlatform.getExpires() - 10) *1000);
+                ()-> registerTask(parentPlatform, sipTransactionInfo),
+                    parentPlatform.getExpires() * 1000);
         }
         }
 
 
 
 
         final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId();
         final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId();
         if (!dynamicTask.contains(keepaliveTaskKey)) {
         if (!dynamicTask.contains(keepaliveTaskKey)) {
+            logger.info("[国标级联]:{}, 添加定时心跳任务", parentPlatform.getServerGBId());
             // 添加心跳任务
             // 添加心跳任务
             dynamicTask.startCron(keepaliveTaskKey,
             dynamicTask.startCron(keepaliveTaskKey,
                     ()-> {
                     ()-> {
@@ -205,11 +284,11 @@ public class PlatformServiceImpl implements IPlatformService {
                             logger.error("[命令发送失败] 国标级联 发送心跳: {}", e.getMessage());
                             logger.error("[命令发送失败] 国标级联 发送心跳: {}", e.getMessage());
                         }
                         }
                     },
                     },
-                    (parentPlatform.getKeepTimeout() - 10)*1000);
+                    (parentPlatform.getKeepTimeout())*1000);
         }
         }
     }
     }
 
 
-    private void registerTask(ParentPlatform parentPlatform){
+    private void registerTask(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo){
         try {
         try {
             // 设置超时重发, 后续从底层支持消息重发
             // 设置超时重发, 后续从底层支持消息重发
             String key = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId() + "_timeout";
             String key = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId() + "_timeout";
@@ -217,10 +296,10 @@ public class PlatformServiceImpl implements IPlatformService {
                 return;
                 return;
             }
             }
             dynamicTask.startDelay(key, ()->{
             dynamicTask.startDelay(key, ()->{
-                registerTask(parentPlatform);
+                registerTask(parentPlatform, sipTransactionInfo);
             }, 1000);
             }, 1000);
-            logger.info("[国标级联] 平台:{}注册即将到期,重新注册", parentPlatform.getServerGBId());
-            commanderForPlatform.register(parentPlatform, eventResult -> {
+            logger.info("[国标级联] 平台:{}注册即将到期,开始续订", parentPlatform.getServerGBId());
+            commanderForPlatform.register(parentPlatform, sipTransactionInfo,  eventResult -> {
                 dynamicTask.stop(key);
                 dynamicTask.stop(key);
                 offline(parentPlatform, false);
                 offline(parentPlatform, false);
             },eventResult -> {
             },eventResult -> {

+ 73 - 16
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java

@@ -438,7 +438,12 @@ public class PlayServiceImpl implements IPlayService {
                 onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId);
                 onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId);
                 hookEvent.response(mediaServerItemInuse, response);
                 hookEvent.response(mediaServerItemInuse, response);
                 logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
                 logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
-                String streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.flv", mediaServerItemInuse.getHttpPort(), "rtp", ssrcInfo.getStream());
+                String streamUrl;
+                if (mediaServerItemInuse.getRtspPort() != 0) {
+                    streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp",  ssrcInfo.getStream());
+                }else {
+                    streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.mp4", mediaServerItemInuse.getHttpPort(), "rtp",  ssrcInfo.getStream());
+                }
                 String path = "snap";
                 String path = "snap";
                 String fileName = device.getDeviceId() + "_" + channelId + ".jpg";
                 String fileName = device.getDeviceId() + "_" + channelId + ".jpg";
                 // 请求截图
                 // 请求截图
@@ -806,23 +811,75 @@ public class PlayServiceImpl implements IPlayService {
             hookCallBack.call(downloadResult);
             hookCallBack.call(downloadResult);
             streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
             streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
         };
         };
-
+        InviteStreamCallback hookEvent = (InviteStreamInfo inviteStreamInfo) -> {
+            logger.info("收到订阅消息: " + inviteStreamInfo.getCallId());
+            dynamicTask.stop(downLoadTimeOutTaskKey);
+            StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
+            streamInfo.setStartTime(startTime);
+            streamInfo.setEndTime(endTime);
+            redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
+            downloadResult.setCode(ErrorCode.SUCCESS.getCode());
+            downloadResult.setMsg(ErrorCode.SUCCESS.getMsg());
+            downloadResult.setData(streamInfo);
+            downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
+            downloadResult.setResponse(inviteStreamInfo.getResponse());
+            hookCallBack.call(downloadResult);
+        };
         try {
         try {
             cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack,
             cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack,
-                    inviteStreamInfo -> {
-                        logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
-                        dynamicTask.stop(downLoadTimeOutTaskKey);
-                        StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
-                        streamInfo.setStartTime(startTime);
-                        streamInfo.setEndTime(endTime);
-                        redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
-                        downloadResult.setCode(ErrorCode.SUCCESS.getCode());
-                        downloadResult.setMsg(ErrorCode.SUCCESS.getMsg());
-                        downloadResult.setData(streamInfo);
-                        downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
-                        downloadResult.setResponse(inviteStreamInfo.getResponse());
-                        hookCallBack.call(downloadResult);
-                    }, errorEvent);
+                    hookEvent, errorEvent, eventResult ->
+                    {
+                        if (eventResult.type == SipSubscribe.EventResultType.response) {
+                            ResponseEvent responseEvent = (ResponseEvent) eventResult.event;
+                            String contentString = new String(responseEvent.getResponse().getRawContent());
+                            // 获取ssrc
+                            int ssrcIndex = contentString.indexOf("y=");
+                            // 检查是否有y字段
+                            if (ssrcIndex >= 0) {
+                                //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
+                                String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
+                                // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
+                                if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
+                                    return;
+                                }
+                                logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
+                                if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
+                                    logger.info("[回放消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
+
+                                    if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
+                                        // ssrc 不可用
+                                        // 释放ssrc
+                                        mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
+                                        streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
+                                        eventResult.msg = "下级自定义了ssrc,但是此ssrc不可用";
+                                        eventResult.statusCode = 400;
+                                        errorEvent.response(eventResult);
+                                        return;
+                                    }
+
+                                    // 单端口模式streamId也有变化,需要重新设置监听
+                                    if (!mediaServerItem.isRtpEnable()) {
+                                        // 添加订阅
+                                        HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
+                                        subscribe.removeSubscribe(hookSubscribe);
+                                        hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
+                                        subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
+                                            logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString());
+                                            dynamicTask.stop(downLoadTimeOutTaskKey);
+                                            // hook响应
+                                            onPublishHandlerForPlayback(mediaServerItemInUse, response, device.getDeviceId(), channelId, hookCallBack);
+                                            hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream()));
+                                        });
+                                    }
+                                    // 关闭rtp server
+                                    mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
+                                    // 重新开启ssrc server
+                                    mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
+                                }
+                            }
+                        }
+
+                    });
         } catch (InvalidArgumentException | SipException | ParseException e) {
         } catch (InvalidArgumentException | SipException | ParseException e) {
             logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
             logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
 
 

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java

@@ -201,7 +201,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
             dataSourceTransactionManager.commit(transactionStatus);     //手动提交
             dataSourceTransactionManager.commit(transactionStatus);     //手动提交
             result = true;
             result = true;
         }catch (Exception e) {
         }catch (Exception e) {
-            e.printStackTrace();
+            logger.error("未处理的异常 ", e);
             dataSourceTransactionManager.rollback(transactionStatus);
             dataSourceTransactionManager.rollback(transactionStatus);
         }
         }
         return result;
         return result;

+ 45 - 9
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java

@@ -1,6 +1,7 @@
 package com.genersoft.iot.vmp.service.redisMsg;
 package com.genersoft.iot.vmp.service.redisMsg;
 
 
 import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSON;
+import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
@@ -44,13 +45,17 @@ public class RedisAlarmMsgListener implements MessageListener {
     @Autowired
     @Autowired
     private ThreadPoolTaskExecutor taskExecutor;
     private ThreadPoolTaskExecutor taskExecutor;
 
 
+    @Autowired
+    private UserSetting userSetting;
+
     @Override
     @Override
     public void onMessage(@NotNull Message message, byte[] bytes) {
     public void onMessage(@NotNull Message message, byte[] bytes) {
+        // 消息示例:  PUBLISH alarm_receive '{ "gbId": "", "alarmSn": 1, "alarmType": "111", "alarmDescription": "222", }'
         logger.info("收到来自REDIS的ALARM通知: {}", new String(message.getBody()));
         logger.info("收到来自REDIS的ALARM通知: {}", new String(message.getBody()));
         boolean isEmpty = taskQueue.isEmpty();
         boolean isEmpty = taskQueue.isEmpty();
         taskQueue.offer(message);
         taskQueue.offer(message);
         if (isEmpty) {
         if (isEmpty) {
-            logger.info("[线程池信息]活动线程数:{}, 最大线程数: {}", taskExecutor.getActiveCount(), taskExecutor.getMaxPoolSize());
+//            logger.info("[线程池信息]活动线程数:{}, 最大线程数: {}", taskExecutor.getActiveCount(), taskExecutor.getMaxPoolSize());
             taskExecutor.execute(() -> {
             taskExecutor.execute(() -> {
                 while (!taskQueue.isEmpty()) {
                 while (!taskQueue.isEmpty()) {
                     Message msg = taskQueue.poll();
                     Message msg = taskQueue.poll();
@@ -69,22 +74,52 @@ public class RedisAlarmMsgListener implements MessageListener {
                         deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn());
                         deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn());
                         deviceAlarm.setAlarmType("" + alarmChannelMessage.getAlarmType());
                         deviceAlarm.setAlarmType("" + alarmChannelMessage.getAlarmType());
                         deviceAlarm.setAlarmPriority("1");
                         deviceAlarm.setAlarmPriority("1");
-                        deviceAlarm.setAlarmTime(DateUtil.getNowForISO8601());
+                        deviceAlarm.setAlarmTime(DateUtil.getNow());
                         deviceAlarm.setLongitude(0);
                         deviceAlarm.setLongitude(0);
                         deviceAlarm.setLatitude(0);
                         deviceAlarm.setLatitude(0);
 
 
                         if (ObjectUtils.isEmpty(gbId)) {
                         if (ObjectUtils.isEmpty(gbId)) {
-                            // 发送给所有的上级
-                            List<ParentPlatform> parentPlatforms = storage.queryEnableParentPlatformList(true);
-                            if (parentPlatforms.size() > 0) {
-                                for (ParentPlatform parentPlatform : parentPlatforms) {
+                            if (userSetting.getSendToPlatformsWhenIdLost()) {
+                                // 发送给所有的上级
+                                List<ParentPlatform> parentPlatforms = storage.queryEnableParentPlatformList(true);
+                                if (parentPlatforms.size() > 0) {
+                                    for (ParentPlatform parentPlatform : parentPlatforms) {
+                                        try {
+                                            deviceAlarm.setChannelId(parentPlatform.getDeviceGBId());
+                                            commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm);
+                                        } catch (SipException | InvalidArgumentException | ParseException e) {
+                                            logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage());
+                                        }
+                                    }
+                                }
+                            }else {
+                                // 获取开启了消息推送的设备和平台
+                                List<ParentPlatform> parentPlatforms = storage.queryEnablePlatformListWithAsMessageChannel();
+                                if (parentPlatforms.size() > 0) {
+                                    for (ParentPlatform parentPlatform : parentPlatforms) {
+                                        try {
+                                            deviceAlarm.setChannelId(parentPlatform.getDeviceGBId());
+                                            commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm);
+                                        } catch (SipException | InvalidArgumentException | ParseException e) {
+                                            logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage());
+                                        }
+                                    }
+                                }
+
+                            }
+                            // 获取开启了消息推送的设备和平台
+                            List<Device> devices = storage.queryDeviceWithAsMessageChannel();
+                            if (devices.size() > 0) {
+                                for (Device device : devices) {
                                     try {
                                     try {
-                                        commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm);
-                                    } catch (SipException | InvalidArgumentException | ParseException e) {
-                                        logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage());
+                                        deviceAlarm.setChannelId(device.getDeviceId());
+                                        commander.sendAlarmMessage(device, deviceAlarm);
+                                    } catch (InvalidArgumentException | SipException | ParseException e) {
+                                        logger.error("[命令发送失败] 发送报警: {}", e.getMessage());
                                     }
                                     }
                                 }
                                 }
                             }
                             }
+
                         }else {
                         }else {
                             Device device = storage.queryVideoDevice(gbId);
                             Device device = storage.queryVideoDevice(gbId);
                             ParentPlatform platform = storage.queryParentPlatByServerGBId(gbId);
                             ParentPlatform platform = storage.queryParentPlatByServerGBId(gbId);
@@ -105,6 +140,7 @@ public class RedisAlarmMsgListener implements MessageListener {
                             }
                             }
                         }
                         }
                     }catch (Exception e) {
                     }catch (Exception e) {
+                        logger.error("未处理的异常 ", e);
                         logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
                         logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
                     }
                     }
                 }
                 }

+ 2 - 1
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java

@@ -202,7 +202,8 @@ public class RedisGbPlayMsgListener implements MessageListener {
 
 
                         }
                         }
                     }catch (Exception e) {
                     }catch (Exception e) {
-                        logger.warn("[RedisGbPlayMsg] 发现未处理的异常, {}",e.getMessage());
+                        logger.warn("[RedisGbPlayMsg] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
+                        logger.error("[RedisGbPlayMsg] 异常内容: ", e);
                     }
                     }
                 }
                 }
             });
             });

+ 2 - 1
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java

@@ -53,7 +53,8 @@ public class RedisGpsMsgListener implements MessageListener {
                         // 只是放入redis缓存起来
                         // 只是放入redis缓存起来
                         redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo);
                         redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo);
                     }catch (Exception e) {
                     }catch (Exception e) {
-                        logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
+                        logger.warn("[REDIS的ALARM通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
+                        logger.error("[REDIS的ALARM通知] 异常内容: ", e);
                     }
                     }
                 }
                 }
             });
             });

+ 2 - 1
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java

@@ -58,7 +58,8 @@ public class RedisPushStreamResponseListener implements MessageListener {
                             responseEvents.get(response.getApp() + response.getStream()).run(response);
                             responseEvents.get(response.getApp() + response.getStream()).run(response);
                         }
                         }
                     }catch (Exception e) {
                     }catch (Exception e) {
-                        logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
+                        logger.warn("[REDIS消息-请求推流结果] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
+                        logger.error("[REDIS消息-请求推流结果] 异常内容: ", e);
                     }
                     }
                 }
                 }
             });
             });

+ 2 - 1
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusListMsgListener.java

@@ -95,7 +95,8 @@ public class RedisPushStreamStatusListMsgListener implements MessageListener {
                             gbStreamService.updateGbIdOrName(streamPushItemForUpdate);
                             gbStreamService.updateGbIdOrName(streamPushItemForUpdate);
                         }
                         }
                     }catch (Exception e) {
                     }catch (Exception e) {
-                        logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
+                        logger.warn("[REDIS消息-推流设备列表更新] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
+                        logger.error("[REDIS消息-推流设备列表更新] 异常内容: ", e);
                     }
                     }
                 }
                 }
             });
             });

+ 2 - 1
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java

@@ -79,7 +79,8 @@ public class RedisPushStreamStatusMsgListener implements MessageListener, Applic
                             streamPushService.online(statusChangeFromPushStream.getOnlineStreams());
                             streamPushService.online(statusChangeFromPushStream.getOnlineStreams());
                         }
                         }
                     }catch (Exception e) {
                     }catch (Exception e) {
-                        logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
+                        logger.warn("[REDIS消息-推流设备状态变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
+                        logger.error("[REDIS消息-推流设备状态变化] 异常内容: ", e);
                     }
                     }
                 }
                 }
             });
             });

+ 2 - 1
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisStreamMsgListener.java

@@ -82,7 +82,8 @@ public class RedisStreamMsgListener implements MessageListener {
                             zlmMediaListManager.removeMedia(app, stream);
                             zlmMediaListManager.removeMedia(app, stream);
                         }
                         }
                     }catch (Exception e) {
                     }catch (Exception e) {
-                        logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
+                        logger.warn("[REDIS消息-流变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
+                        logger.error("[REDIS消息-流变化] 异常内容: ", e);
                     }
                     }
                 }
                 }
             });
             });

+ 8 - 1
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java

@@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
 import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
 import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
 import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
 import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
+import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
 import com.github.pagehelper.PageInfo;
 import com.github.pagehelper.PageInfo;
 
 
 import java.util.List;
 import java.util.List;
@@ -58,7 +59,7 @@ public interface IVideoManagerStorage {
 	 */
 	 */
 	public PageInfo<DeviceChannel> queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count);
 	public PageInfo<DeviceChannel> queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count);
 	
 	
-	public List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit,List<String> channelIds);
+	public List<DeviceChannelExtend> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, List<String> channelIds, String query, Boolean hasSubChannel, Boolean online, int start, int limit);
 
 
 
 
 	/**
 	/**
@@ -374,4 +375,10 @@ public interface IVideoManagerStorage {
 	void cleanContentForPlatform(String serverGBId);
 	void cleanContentForPlatform(String serverGBId);
 
 
 	List<DeviceChannel> queryChannelWithCatalog(String serverGBId);
 	List<DeviceChannel> queryChannelWithCatalog(String serverGBId);
+
+	List<DeviceChannelExtend> queryChannelsByDeviceId(String serial, List<String> channelIds, Boolean online);
+
+	List<ParentPlatform> queryEnablePlatformListWithAsMessageChannel();
+
+	List<Device> queryDeviceWithAsMessageChannel();
 }
 }

+ 53 - 25
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java

@@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannelInPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannelInPlatform;
 import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
 import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
 import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
 import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
+import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
 import org.apache.ibatis.annotations.*;
 import org.apache.ibatis.annotations.*;
 import org.springframework.stereotype.Repository;
 import org.springframework.stereotype.Repository;
 
 
@@ -82,7 +83,56 @@ public interface DeviceChannelMapper {
             "</foreach> </if>" +
             "</foreach> </if>" +
             "ORDER BY dc.channelId " +
             "ORDER BY dc.channelId " +
             " </script>"})
             " </script>"})
-    List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online,List<String> channelIds);
+    List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, List<String> channelIds);
+
+    @Select(value = {" <script>" +
+            "SELECT " +
+            "dc.*, " +
+            "de.name as deviceName, " +
+            "de.online as deviceOnline " +
+            "from " +
+            "device_channel dc " +
+            "LEFT JOIN device de ON dc.deviceId = de.deviceId " +
+            "WHERE 1=1" +
+            " <if test='deviceId != null'> AND dc.deviceId = #{deviceId} </if> " +
+            " <if test='query != null'> AND (dc.channelId LIKE '%${query}%' OR dc.name LIKE '%${query}%' OR dc.name LIKE '%${query}%')</if> " +
+            " <if test='parentChannelId != null'> AND dc.parentId=#{parentChannelId} </if> " +
+            " <if test='online == true' > AND dc.status=1</if>" +
+            " <if test='online == false' > AND dc.status=0</if>" +
+            " <if test='hasSubChannel == true' >  AND dc.subCount > 0 </if>" +
+            " <if test='hasSubChannel == false' >  AND dc.subCount = 0 </if>" +
+            "<if test='channelIds != null'> AND dc.channelId in <foreach item='item' index='index' collection='channelIds' open='(' separator=',' close=')'>" +
+            "#{item} " +
+            "</foreach> </if>" +
+            "ORDER BY dc.channelId ASC" +
+            " </script>"})
+    List<DeviceChannelExtend> queryChannelsWithDeviceInfo(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, List<String> channelIds);
+
+
+    @Select(value = {" <script>" +
+            "SELECT " +
+            "dc.*, " +
+            "de.name as deviceName, " +
+            "de.online as deviceOnline " +
+            "from " +
+            "device_channel dc " +
+            "LEFT JOIN device de ON dc.deviceId = de.deviceId " +
+            "WHERE 1=1" +
+            " <if test='deviceId != null'> AND dc.deviceId = #{deviceId} </if> " +
+            " <if test='query != null'> AND (dc.channelId LIKE '%${query}%' OR dc.name LIKE '%${query}%' OR dc.name LIKE '%${query}%')</if> " +
+            " <if test='parentChannelId != null'> AND dc.parentId=#{parentChannelId} </if> " +
+            " <if test='online == true' > AND dc.status=1</if>" +
+            " <if test='online == false' > AND dc.status=0</if>" +
+            " <if test='hasSubChannel == true' >  AND dc.subCount > 0 </if>" +
+            " <if test='hasSubChannel == false' >  AND dc.subCount = 0 </if>" +
+            "<if test='channelIds != null'> AND dc.channelId in <foreach item='item' index='index' collection='channelIds' open='(' separator=',' close=')'>" +
+            "#{item} " +
+            "</foreach> </if>" +
+            "ORDER BY dc.channelId ASC " +
+            "Limit #{limit} OFFSET #{start}" +
+            " </script>"})
+    List<DeviceChannelExtend> queryChannelsByDeviceIdWithStartAndLimit(String deviceId,List<String> channelIds, String parentChannelId, String query,
+                                                                       Boolean hasSubChannel, Boolean online, int start, int limit);
 
 
     @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND channelId=#{channelId}")
     @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND channelId=#{channelId}")
     DeviceChannel queryChannel(String deviceId, String channelId);
     DeviceChannel queryChannel(String deviceId, String channelId);
@@ -245,28 +295,6 @@ public interface DeviceChannelMapper {
     int batchUpdate(List<DeviceChannel> updateChannels);
     int batchUpdate(List<DeviceChannel> updateChannels);
 
 
 
 
-    @Select(value = {" <script>" +
-            "SELECT " +
-            "dc1.* " +
-            "from " +
-            "device_channel dc1 " +
-            "WHERE " +
-            "dc1.deviceId = #{deviceId} " +
-            " <if test='query != null'> AND (dc1.channelId LIKE concat('%',#{query},'%') OR dc1.name LIKE concat('%',#{query},'%') OR dc1.name LIKE concat('%',#{query},'%'))</if> " +
-            " <if test='parentChannelId != null'> AND dc1.parentId=#{parentChannelId} </if> " +
-            " <if test='online == true' > AND dc1.status=1</if>" +
-            " <if test='online == false' > AND dc1.status=0</if>" +
-            " <if test='hasSubChannel == true' >  AND dc1.subCount >0</if>" +
-            " <if test='hasSubChannel == false' >  AND dc1.subCount=0</if>" +
-            "<if test='channelIds != null'> AND dc1.channelId in <foreach item='item' index='index' collection='channelIds' open='(' separator=',' close=')'>" +
-            "#{item} " +
-            "</foreach> </if>" +
-            "ORDER BY dc1.channelId ASC " +
-            "Limit #{limit} OFFSET #{start}" +
-            " </script>"})
-    List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String parentChannelId, String query,
-                                                                 Boolean hasSubChannel, Boolean online, int start, int limit,List<String> channelIds);
-
     @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND status=1")
     @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND status=1")
     List<DeviceChannel> queryOnlineChannelsByDeviceId(String deviceId);
     List<DeviceChannel> queryOnlineChannelsByDeviceId(String deviceId);
 
 
@@ -316,10 +344,10 @@ public interface DeviceChannelMapper {
             "select * " +
             "select * " +
             "from device_channel " +
             "from device_channel " +
             "where deviceId=#{deviceId}" +
             "where deviceId=#{deviceId}" +
-            " <if test='parentId != null and length != null' > and parentId = #{parentId} or left(channelId, #{parentId.length()}) = #{parentId} and length(channelId)=#{length} </if>" +
+            " <if test='parentId != null and length != null' > and parentId = #{parentId} or left(channelId, LENGTH(#{parentId})) = #{parentId} and length(channelId)=#{length} </if>" +
             " <if test='parentId == null and length != null' > and parentId = #{parentId} or length(channelId)=#{length} </if>" +
             " <if test='parentId == null and length != null' > and parentId = #{parentId} or length(channelId)=#{length} </if>" +
             " <if test='parentId == null and length == null' > and parentId = #{parentId} </if>" +
             " <if test='parentId == null and length == null' > and parentId = #{parentId} </if>" +
-            " <if test='parentId != null and length == null' > and parentId = #{parentId} or left(channelId, #{parentId.length()}) = #{parentId} </if>" +
+            " <if test='parentId != null and length == null' > and parentId = #{parentId} or left(channelId, LENGTH(#{parentId})) = #{parentId} </if>" +
             " </script>"})
             " </script>"})
     List<DeviceChannel> getChannelsWithCivilCodeAndLength(String deviceId, String parentId, Integer length);
     List<DeviceChannel> getChannelsWithCivilCodeAndLength(String deviceId, String parentId, Integer length);
 
 

+ 21 - 7
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java

@@ -39,9 +39,12 @@ public interface DeviceMapper {
             "mobilePositionSubmissionInterval," +
             "mobilePositionSubmissionInterval," +
             "subscribeCycleForAlarm," +
             "subscribeCycleForAlarm," +
             "ssrcCheck," +
             "ssrcCheck," +
+            "asMessageChannel," +
             "geoCoordSys," +
             "geoCoordSys," +
             "treeType," +
             "treeType," +
-            "online" +
+            "online," +
+            "mediaServerId," +
+            "(SELECT count(0) FROM device_channel WHERE deviceId=device.deviceId) as channelCount "+
             " FROM device WHERE deviceId = #{deviceId}")
             " FROM device WHERE deviceId = #{deviceId}")
     Device getDeviceByDeviceId(String deviceId);
     Device getDeviceByDeviceId(String deviceId);
 
 
@@ -70,6 +73,7 @@ public interface DeviceMapper {
                 "mobilePositionSubmissionInterval," +
                 "mobilePositionSubmissionInterval," +
                 "subscribeCycleForAlarm," +
                 "subscribeCycleForAlarm," +
                 "ssrcCheck," +
                 "ssrcCheck," +
+                "asMessageChannel," +
                 "geoCoordSys," +
                 "geoCoordSys," +
                 "treeType," +
                 "treeType," +
                 "online" +
                 "online" +
@@ -98,6 +102,7 @@ public interface DeviceMapper {
                 "#{mobilePositionSubmissionInterval}," +
                 "#{mobilePositionSubmissionInterval}," +
                 "#{subscribeCycleForAlarm}," +
                 "#{subscribeCycleForAlarm}," +
                 "#{ssrcCheck}," +
                 "#{ssrcCheck}," +
+                "#{asMessageChannel}," +
                 "#{geoCoordSys}," +
                 "#{geoCoordSys}," +
                 "#{treeType}," +
                 "#{treeType}," +
                 "#{online}" +
                 "#{online}" +
@@ -152,9 +157,11 @@ public interface DeviceMapper {
             "mobilePositionSubmissionInterval," +
             "mobilePositionSubmissionInterval," +
             "subscribeCycleForAlarm," +
             "subscribeCycleForAlarm," +
             "ssrcCheck," +
             "ssrcCheck," +
+            "asMessageChannel," +
             "geoCoordSys," +
             "geoCoordSys," +
             "treeType," +
             "treeType," +
             "online," +
             "online," +
+            "mediaServerId," +
             "(SELECT count(0) FROM device_channel WHERE deviceId=de.deviceId) as channelCount  FROM device de" +
             "(SELECT count(0) FROM device_channel WHERE deviceId=de.deviceId) as channelCount  FROM device de" +
             "<if test=\"online != null\"> where online=${online}</if>"+
             "<if test=\"online != null\"> where online=${online}</if>"+
             " </script>"
             " </script>"
@@ -164,9 +171,6 @@ public interface DeviceMapper {
     @Delete("DELETE FROM device WHERE deviceId=#{deviceId}")
     @Delete("DELETE FROM device WHERE deviceId=#{deviceId}")
     int del(String deviceId);
     int del(String deviceId);
 
 
-    @Update("UPDATE device SET online=0")
-    int outlineForAll();
-
     @Select("SELECT " +
     @Select("SELECT " +
             "deviceId, " +
             "deviceId, " +
             "coalesce(custom_name, name) as name, " +
             "coalesce(custom_name, name) as name, " +
@@ -192,6 +196,7 @@ public interface DeviceMapper {
             "mobilePositionSubmissionInterval," +
             "mobilePositionSubmissionInterval," +
             "subscribeCycleForAlarm," +
             "subscribeCycleForAlarm," +
             "ssrcCheck," +
             "ssrcCheck," +
+            "asMessageChannel," +
             "geoCoordSys," +
             "geoCoordSys," +
             "treeType," +
             "treeType," +
             "online " +
             "online " +
@@ -222,6 +227,7 @@ public interface DeviceMapper {
             "mobilePositionSubmissionInterval," +
             "mobilePositionSubmissionInterval," +
             "subscribeCycleForAlarm," +
             "subscribeCycleForAlarm," +
             "ssrcCheck," +
             "ssrcCheck," +
+            "asMessageChannel," +
             "geoCoordSys," +
             "geoCoordSys," +
             "treeType," +
             "treeType," +
             "online" +
             "online" +
@@ -243,12 +249,13 @@ public interface DeviceMapper {
             "<if test=\"mobilePositionSubmissionInterval != null\">, mobilePositionSubmissionInterval=#{mobilePositionSubmissionInterval}</if>" +
             "<if test=\"mobilePositionSubmissionInterval != null\">, mobilePositionSubmissionInterval=#{mobilePositionSubmissionInterval}</if>" +
             "<if test=\"subscribeCycleForAlarm != null\">, subscribeCycleForAlarm=#{subscribeCycleForAlarm}</if>" +
             "<if test=\"subscribeCycleForAlarm != null\">, subscribeCycleForAlarm=#{subscribeCycleForAlarm}</if>" +
             "<if test=\"ssrcCheck != null\">, ssrcCheck=#{ssrcCheck}</if>" +
             "<if test=\"ssrcCheck != null\">, ssrcCheck=#{ssrcCheck}</if>" +
+            "<if test=\"asMessageChannel != null\">, asMessageChannel=#{asMessageChannel}</if>" +
             "<if test=\"geoCoordSys != null\">, geoCoordSys=#{geoCoordSys}</if>" +
             "<if test=\"geoCoordSys != null\">, geoCoordSys=#{geoCoordSys}</if>" +
             "<if test=\"treeType != null\">, treeType=#{treeType}</if>" +
             "<if test=\"treeType != null\">, treeType=#{treeType}</if>" +
             "<if test=\"mediaServerId != null\">, mediaServerId=#{mediaServerId}</if>" +
             "<if test=\"mediaServerId != null\">, mediaServerId=#{mediaServerId}</if>" +
             "WHERE deviceId=#{deviceId}"+
             "WHERE deviceId=#{deviceId}"+
             " </script>"})
             " </script>"})
-    int updateCustom(Device device);
+    void updateCustom(Device device);
 
 
     @Insert("INSERT INTO device (" +
     @Insert("INSERT INTO device (" +
             "deviceId, " +
             "deviceId, " +
@@ -259,9 +266,11 @@ public interface DeviceMapper {
             "updateTime," +
             "updateTime," +
             "charset," +
             "charset," +
             "ssrcCheck," +
             "ssrcCheck," +
+            "asMessageChannel," +
             "geoCoordSys," +
             "geoCoordSys," +
             "treeType," +
             "treeType," +
-            "online" +
+            "online," +
+            "mediaServerId" +
             ") VALUES (" +
             ") VALUES (" +
             "#{deviceId}," +
             "#{deviceId}," +
             "#{name}," +
             "#{name}," +
@@ -271,9 +280,11 @@ public interface DeviceMapper {
             "#{updateTime}," +
             "#{updateTime}," +
             "#{charset}," +
             "#{charset}," +
             "#{ssrcCheck}," +
             "#{ssrcCheck}," +
+            "#{asMessageChannel}," +
             "#{geoCoordSys}," +
             "#{geoCoordSys}," +
             "#{treeType}," +
             "#{treeType}," +
-            "#{online}" +
+            "#{online}," +
+            "#{mediaServerId}" +
             ")")
             ")")
     void addCustomDevice(Device device);
     void addCustomDevice(Device device);
 
 
@@ -282,4 +293,7 @@ public interface DeviceMapper {
 
 
     @Select("select * from device")
     @Select("select * from device")
     List<Device> getAll();
     List<Device> getAll();
+
+    @Select("select * from device where  asMessageChannel = 1")
+    List<Device> queryDeviceWithAsMessageChannel();
 }
 }

+ 7 - 3
src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java

@@ -15,10 +15,10 @@ import java.util.List;
 public interface ParentPlatformMapper {
 public interface ParentPlatformMapper {
 
 
     @Insert("INSERT INTO parent_platform (enable, name, serverGBId, serverGBDomain, serverIP, serverPort, deviceGBId, deviceIp,  " +
     @Insert("INSERT INTO parent_platform (enable, name, serverGBId, serverGBDomain, serverIP, serverPort, deviceGBId, deviceIp,  " +
-            "            devicePort, username, password, expires, keepTimeout, transport, characterSet, ptz, rtcp, " +
+            "            devicePort, username, password, expires, keepTimeout, transport, characterSet, ptz, rtcp, asMessageChannel, " +
             "            status, startOfflinePush, catalogId, administrativeDivision, catalogGroup, createTime, updateTime, treeType) " +
             "            status, startOfflinePush, catalogId, administrativeDivision, catalogGroup, createTime, updateTime, treeType) " +
             "            VALUES (#{enable}, #{name}, #{serverGBId}, #{serverGBDomain}, #{serverIP}, #{serverPort}, #{deviceGBId}, #{deviceIp}, " +
             "            VALUES (#{enable}, #{name}, #{serverGBId}, #{serverGBDomain}, #{serverIP}, #{serverPort}, #{deviceGBId}, #{deviceIp}, " +
-            "            #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, " +
+            "            #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, #{asMessageChannel}, " +
             "            #{status},  #{startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime}, #{treeType})")
             "            #{status},  #{startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime}, #{treeType})")
     int addParentPlatform(ParentPlatform parentPlatform);
     int addParentPlatform(ParentPlatform parentPlatform);
 
 
@@ -40,6 +40,7 @@ public interface ParentPlatformMapper {
             "characterSet=#{characterSet}, " +
             "characterSet=#{characterSet}, " +
             "ptz=#{ptz}, " +
             "ptz=#{ptz}, " +
             "rtcp=#{rtcp}, " +
             "rtcp=#{rtcp}, " +
+            "asMessageChannel=#{asMessageChannel}, " +
             "status=#{status}, " +
             "status=#{status}, " +
             "startOfflinePush=#{startOfflinePush}, " +
             "startOfflinePush=#{startOfflinePush}, " +
             "catalogGroup=#{catalogGroup}, " +
             "catalogGroup=#{catalogGroup}, " +
@@ -68,9 +69,12 @@ public interface ParentPlatformMapper {
             "FROM parent_platform pp ")
             "FROM parent_platform pp ")
     List<ParentPlatform> getParentPlatformList();
     List<ParentPlatform> getParentPlatformList();
 
 
-    @Select("SELECT * FROM parent_platform WHERE enable=#{enable}")
+    @Select("SELECT * FROM parent_platform WHERE enable=#{enable} ")
     List<ParentPlatform> getEnableParentPlatformList(boolean enable);
     List<ParentPlatform> getEnableParentPlatformList(boolean enable);
 
 
+    @Select("SELECT * FROM parent_platform WHERE enable=1 and asMessageChannel = 1")
+    List<ParentPlatform> queryEnablePlatformListWithAsMessageChannel();
+
     @Select("SELECT * FROM parent_platform WHERE serverGBId=#{platformGbId}")
     @Select("SELECT * FROM parent_platform WHERE serverGBId=#{platformGbId}")
     ParentPlatform getParentPlatByServerGBId(String platformGbId);
     ParentPlatform getParentPlatByServerGBId(String platformGbId);
 
 

+ 9 - 6
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java

@@ -177,12 +177,14 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
     @Override
     @Override
     public boolean startDownload(StreamInfo stream, String callId) {
     public boolean startDownload(StreamInfo stream, String callId) {
         boolean result;
         boolean result;
+        String key=String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
+                userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId);
         if (stream.getProgress() == 1) {
         if (stream.getProgress() == 1) {
-            result = RedisUtil.set(String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
-                    userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream);
+            logger.debug("添加下载缓存==已完成下载=》{}",key);
+            result = RedisUtil.set(key, stream);
         }else {
         }else {
-            result = RedisUtil.set(String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
-                    userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream, 60*60);
+            logger.debug("添加下载缓存==未完成下载=》{}",key);
+            result = RedisUtil.set(key, stream, 60*60);
         }
         }
         return result;
         return result;
     }
     }
@@ -617,7 +619,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
                 stream,
                 stream,
                 callId
                 callId
         );
         );
-        List<Object> streamInfoScan = RedisUtil.scan(key);
+        List<Object> streamInfoScan = RedisUtil.scan2(key);
         if (streamInfoScan.size() > 0) {
         if (streamInfoScan.size() > 0) {
             return (StreamInfo) RedisUtil.get((String) streamInfoScan.get(0));
             return (StreamInfo) RedisUtil.get((String) streamInfoScan.get(0));
         }else {
         }else {
@@ -855,7 +857,8 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
 
 
     @Override
     @Override
     public void sendAlarmMsg(AlarmChannelMessage msg) {
     public void sendAlarmMsg(AlarmChannelMessage msg) {
-        String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE;
+        // 此消息用于对接第三方服务下级来的消息内容
+        String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM;
         logger.info("[redis发送通知] 报警{}: {}", key, JSON.toJSON(msg));
         logger.info("[redis发送通知] 报警{}: {}", key, JSON.toJSON(msg));
         RedisUtil.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
         RedisUtil.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
     }
     }

+ 20 - 4
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java

@@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.storager.dao.*;
 import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
 import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
 import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
+import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import com.github.pagehelper.PageInfo;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
@@ -189,7 +190,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
 			dataSourceTransactionManager.commit(transactionStatus);     //手动提交
 			dataSourceTransactionManager.commit(transactionStatus);     //手动提交
 			return true;
 			return true;
 		}catch (Exception e) {
 		}catch (Exception e) {
-			e.printStackTrace();
+			logger.error("未处理的异常 ", e);
 			dataSourceTransactionManager.rollback(transactionStatus);
 			dataSourceTransactionManager.rollback(transactionStatus);
 			return false;
 			return false;
 		}
 		}
@@ -305,7 +306,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
 			}
 			}
 			return true;
 			return true;
 		}catch (Exception e) {
 		}catch (Exception e) {
-			e.printStackTrace();
+			logger.error("未处理的异常 ", e);
 			dataSourceTransactionManager.rollback(transactionStatus);
 			dataSourceTransactionManager.rollback(transactionStatus);
 			return false;
 			return false;
 		}
 		}
@@ -359,8 +360,8 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
 	}
 	}
 
 
 	@Override
 	@Override
-	public List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit,List<String> channelIds) {
-		return deviceChannelMapper.queryChannelsByDeviceIdWithStartAndLimit(deviceId, null, query, hasSubChannel, online, start, limit,channelIds);
+	public List<DeviceChannelExtend> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, List<String> channelIds, String query, Boolean hasSubChannel, Boolean online, int start, int limit) {
+		return deviceChannelMapper.queryChannelsByDeviceIdWithStartAndLimit(deviceId, channelIds, null, query, hasSubChannel, online, start, limit);
 	}
 	}
 
 
 
 
@@ -369,6 +370,11 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
 		return deviceChannelMapper.queryChannels(deviceId, null,null, null, online,channelIds);
 		return deviceChannelMapper.queryChannels(deviceId, null,null, null, online,channelIds);
 	}
 	}
 
 
+	@Override
+	public List<DeviceChannelExtend> queryChannelsByDeviceId(String deviceId, List<String> channelIds, Boolean online) {
+		return deviceChannelMapper.queryChannelsWithDeviceInfo(deviceId, null,null, null, online,channelIds);
+	}
+
 	@Override
 	@Override
 	public PageInfo<DeviceChannel> querySubChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, int page, int count) {
 	public PageInfo<DeviceChannel> querySubChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, int page, int count) {
 		PageHelper.startPage(page, count);
 		PageHelper.startPage(page, count);
@@ -511,6 +517,16 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
 		return platformMapper.getEnableParentPlatformList(enable);
 		return platformMapper.getEnableParentPlatformList(enable);
 	}
 	}
 
 
+	@Override
+	public List<ParentPlatform> queryEnablePlatformListWithAsMessageChannel() {
+		return platformMapper.queryEnablePlatformListWithAsMessageChannel();
+	}
+
+	@Override
+	public List<Device> queryDeviceWithAsMessageChannel() {
+		return deviceMapper.queryDeviceWithAsMessageChannel();
+	}
+
 	@Override
 	@Override
 	public void outlineForAllParentPlatform() {
 	public void outlineForAllParentPlatform() {
 		platformMapper.outlineForAllParentPlatform();
 		platformMapper.outlineForAllParentPlatform();

+ 0 - 1
src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java

@@ -45,7 +45,6 @@ public class DateUtil {
 	
 	
 	public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) {
 	public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) {
         return formatter.format(formatterCompatibleISO8601.parse(formatTime));
         return formatter.format(formatterCompatibleISO8601.parse(formatTime));
-
     }
     }
 
 
     /**
     /**

+ 7 - 1
src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java

@@ -881,7 +881,13 @@ public class RedisUtil {
 
 
         return new ArrayList<>(resultKeys);
         return new ArrayList<>(resultKeys);
     }
     }
-
+    public static List<Object> scan2(String query) {
+        if (redisTemplate == null) {
+            redisTemplate = SpringBeanFactory.getBean("redisTemplate");
+        }
+        Set<String> keys = redisTemplate.keys(query);
+        return new ArrayList<>(keys);
+    }
     //    ============================== 消息发送与订阅 ==============================
     //    ============================== 消息发送与订阅 ==============================
     public static void convertAndSend(String channel, JSONObject msg) {
     public static void convertAndSend(String channel, JSONObject msg) {
         if (redisTemplate == null) {
         if (redisTemplate == null) {

+ 4 - 0
src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java

@@ -28,6 +28,10 @@ public class WVPResult<T> implements Cloneable{
         return new WVPResult<>(ErrorCode.SUCCESS.getCode(), msg, t);
         return new WVPResult<>(ErrorCode.SUCCESS.getCode(), msg, t);
     }
     }
 
 
+    public static WVPResult success() {
+        return new WVPResult<>(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), null);
+    }
+
     public static <T> WVPResult<T> success(T t) {
     public static <T> WVPResult<T> success(T t) {
         return success(t, ErrorCode.SUCCESS.getMsg());
         return success(t, ErrorCode.SUCCESS.getMsg());
     }
     }

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java

@@ -30,7 +30,7 @@ import java.util.UUID;
  *  位置信息管理
  *  位置信息管理
  */
  */
 @Tag(name  = "位置信息管理")
 @Tag(name  = "位置信息管理")
-@CrossOrigin
+
 @RestController
 @RestController
 @RequestMapping("/api/position")
 @RequestMapping("/api/position")
 public class MobilePositionController {
 public class MobilePositionController {

+ 1 - 1
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;
  * @data: 2021-01-20
  * @data: 2021-01-20
  */
  */
 @Tag(name  = "SSE推送")
 @Tag(name  = "SSE推送")
-@CrossOrigin
+
 @Controller
 @Controller
 @RequestMapping("/api")
 @RequestMapping("/api")
 public class SseController {
 public class SseController {

+ 11 - 17
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java

@@ -6,35 +6,28 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
-import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookListener;
 import com.genersoft.iot.vmp.service.IDeviceAlarmService;
 import com.genersoft.iot.vmp.service.IDeviceAlarmService;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
-import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import com.github.pagehelper.PageInfo;
 import com.github.pagehelper.PageInfo;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.responses.ApiResponse;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.InvalidArgumentException;
 import javax.sip.SipException;
 import javax.sip.SipException;
 import java.text.ParseException;
 import java.text.ParseException;
-import java.time.LocalDateTime;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.List;
 import java.util.List;
 
 
 @Tag(name = "报警信息管理")
 @Tag(name = "报警信息管理")
-@CrossOrigin
+
 @RestController
 @RestController
 @RequestMapping("/api/alarm")
 @RequestMapping("/api/alarm")
 public class AlarmController {
 public class AlarmController {
@@ -78,11 +71,11 @@ public class AlarmController {
         if (ObjectUtils.isEmpty(deviceIds)) {
         if (ObjectUtils.isEmpty(deviceIds)) {
             deviceIds = null;
             deviceIds = null;
         }
         }
+
         if (ObjectUtils.isEmpty(time)) {
         if (ObjectUtils.isEmpty(time)) {
             time = null;
             time = null;
-        }
-        if (!DateUtil.verification(time, DateUtil.formatter) ){
-            return null;
+        }else if (!DateUtil.verification(time, DateUtil.formatter) ){
+            throw new ControllerException(ErrorCode.ERROR400.getCode(), "time格式为" + DateUtil.PATTERN);
         }
         }
         List<String> deviceIdList = null;
         List<String> deviceIdList = null;
         if (deviceIds != null) {
         if (deviceIds != null) {
@@ -110,7 +103,7 @@ public class AlarmController {
         deviceAlarm.setAlarmDescription("test");
         deviceAlarm.setAlarmDescription("test");
         deviceAlarm.setAlarmMethod("1");
         deviceAlarm.setAlarmMethod("1");
         deviceAlarm.setAlarmPriority("1");
         deviceAlarm.setAlarmPriority("1");
-        deviceAlarm.setAlarmTime(DateUtil.formatterISO8601.format(LocalDateTime.now()));
+        deviceAlarm.setAlarmTime(DateUtil.getNow());
         deviceAlarm.setAlarmType("1");
         deviceAlarm.setAlarmType("1");
         deviceAlarm.setLongitude(115.33333);
         deviceAlarm.setLongitude(115.33333);
         deviceAlarm.setLatitude(39.33333);
         deviceAlarm.setLatitude(39.33333);
@@ -177,16 +170,17 @@ public class AlarmController {
         if (ObjectUtils.isEmpty(alarmType)) {
         if (ObjectUtils.isEmpty(alarmType)) {
             alarmType = null;
             alarmType = null;
         }
         }
+
         if (ObjectUtils.isEmpty(startTime)) {
         if (ObjectUtils.isEmpty(startTime)) {
             startTime = null;
             startTime = null;
+        }else if (!DateUtil.verification(startTime, DateUtil.formatter) ){
+            throw new ControllerException(ErrorCode.ERROR400.getCode(), "startTime格式为" + DateUtil.PATTERN);
         }
         }
+
         if (ObjectUtils.isEmpty(endTime)) {
         if (ObjectUtils.isEmpty(endTime)) {
             endTime = null;
             endTime = null;
-        }
-
-
-        if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){
-            throw new ControllerException(ErrorCode.ERROR100.getCode(), "开始时间或结束时间格式有误");
+        }else if (!DateUtil.verification(endTime, DateUtil.formatter) ){
+            throw new ControllerException(ErrorCode.ERROR400.getCode(), "endTime格式为" + DateUtil.PATTERN);
         }
         }
 
 
         return deviceAlarmService.getAllAlarm(page, count, deviceId, alarmPriority, alarmMethod,
         return deviceAlarmService.getAllAlarm(page, count, deviceId, alarmPriority, alarmMethod,

+ 0 - 4
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;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
-
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.Parameter;
@@ -22,9 +21,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.ResponseEntity;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.context.request.async.DeferredResult;
 import org.springframework.web.context.request.async.DeferredResult;
 
 
@@ -34,7 +31,6 @@ import java.text.ParseException;
 import java.util.UUID;
 import java.util.UUID;
 
 
 @Tag(name = "国标设备配置")
 @Tag(name = "国标设备配置")
-@CrossOrigin
 @RestController
 @RestController
 @RequestMapping("/api/device/config")
 @RequestMapping("/api/device/config")
 public class DeviceConfig {
 public class DeviceConfig {

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java

@@ -32,7 +32,7 @@ import java.text.ParseException;
 import java.util.UUID;
 import java.util.UUID;
 
 
 @Tag(name  = "国标设备控制")
 @Tag(name  = "国标设备控制")
-@CrossOrigin
+
 @RestController
 @RestController
 @RequestMapping("/api/device/control")
 @RequestMapping("/api/device/control")
 public class DeviceControl {
 public class DeviceControl {

+ 5 - 2
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java

@@ -24,6 +24,7 @@ import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.apache.commons.compress.utils.IOUtils;
 import org.apache.commons.compress.utils.IOUtils;
+import org.apache.ibatis.annotations.Options;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -46,7 +47,7 @@ import java.util.*;
 
 
 @Tag(name  = "国标设备查询", description = "国标设备查询")
 @Tag(name  = "国标设备查询", description = "国标设备查询")
 @SuppressWarnings("rawtypes")
 @SuppressWarnings("rawtypes")
-@CrossOrigin
+
 @RestController
 @RestController
 @RequestMapping("/api/device/query")
 @RequestMapping("/api/device/query")
 public class DeviceQuery {
 public class DeviceQuery {
@@ -97,8 +98,10 @@ public class DeviceQuery {
 	@Parameter(name = "page", description = "当前页", required = true)
 	@Parameter(name = "page", description = "当前页", required = true)
 	@Parameter(name = "count", description = "每页查询数量", required = true)
 	@Parameter(name = "count", description = "每页查询数量", required = true)
 	@GetMapping("/devices")
 	@GetMapping("/devices")
+	@Options()
 	public PageInfo<Device> devices(int page, int count){
 	public PageInfo<Device> devices(int page, int count){
-		
+//		if (page == null) page = 0;
+//		if (count == null) count = 20;
 		return storager.queryVideoDeviceList(page, count,null);
 		return storager.queryVideoDeviceList(page, count,null);
 	}
 	}
 
 

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java

@@ -17,7 +17,7 @@ import org.springframework.web.bind.annotation.*;
 import java.util.List;
 import java.util.List;
 
 
 @Tag(name  = "视频流关联到级联平台")
 @Tag(name  = "视频流关联到级联平台")
-@CrossOrigin
+
 @RestController
 @RestController
 @RequestMapping("/api/gbStream")
 @RequestMapping("/api/gbStream")
 public class GbStreamController {
 public class GbStreamController {

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java

@@ -24,7 +24,7 @@ import javax.servlet.http.HttpServletRequest;
 
 
 @Tag(name  = "媒体流相关")
 @Tag(name  = "媒体流相关")
 @Controller
 @Controller
-@CrossOrigin
+
 @RequestMapping(value = "/api/media")
 @RequestMapping(value = "/api/media")
 public class MediaController {
 public class MediaController {
 
 

+ 9 - 54
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java

@@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
 import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
 import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
 import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
 import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
@@ -37,7 +38,7 @@ import java.util.List;
  * 级联平台管理
  * 级联平台管理
  */
  */
 @Tag(name  = "级联平台管理")
 @Tag(name  = "级联平台管理")
-@CrossOrigin
+
 @RestController
 @RestController
 @RequestMapping("/api/platform")
 @RequestMapping("/api/platform")
 public class PlatformController {
 public class PlatformController {
@@ -205,58 +206,8 @@ public class PlatformController {
         ) {
         ) {
             throw new ControllerException(ErrorCode.ERROR400);
             throw new ControllerException(ErrorCode.ERROR400);
         }
         }
-        parentPlatform.setCharacterSet(parentPlatform.getCharacterSet().toUpperCase());
-        ParentPlatform parentPlatformOld = storager.queryParentPlatByServerGBId(parentPlatform.getServerGBId());
-        parentPlatform.setUpdateTime(DateUtil.getNow());
-        if (!parentPlatformOld.getTreeType().equals(parentPlatform.getTreeType())) {
-             // 目录结构发生变化,清空之前的关联关系
-             logger.info("保存平台{}时发现目录结构变化,清空关联关系", parentPlatform.getDeviceGBId());
-             storager.cleanContentForPlatform(parentPlatform.getServerGBId());
-
-        }
-        boolean updateResult = storager.updateParentPlatform(parentPlatform);
-
-        if (updateResult) {
-            // 保存时启用就发送注册
-            if (parentPlatform.isEnable()) {
-                if (parentPlatformOld != null && parentPlatformOld.isStatus()) {
-                    try {
-                        commanderForPlatform.unregister(parentPlatformOld, null, null);
-                    } catch (InvalidArgumentException | ParseException | SipException e) {
-                        logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
-                    }
-                    try {
-                        Thread.sleep(500);
-                    } catch (InterruptedException e) {
-                        logger.error("[线程休眠失败] : {}", e.getMessage());
-                    }
-                    //  只要保存就发送注册
-                    try {
-                        commanderForPlatform.register(parentPlatform, null, null);
-                    } catch (InvalidArgumentException | ParseException | SipException e) {
-                        logger.error("[命令发送失败] 国标级联 注册: {}", e.getMessage());
-                    }
-
-                } else {
-                    //  只要保存就发送注册
-                    try {
-                        commanderForPlatform.register(parentPlatform, null, null);
-                    } catch (InvalidArgumentException | ParseException | SipException e) {
-                        logger.error("[命令发送失败] 国标级联 注册: {}", e.getMessage());
-                    }
-                }
-            } else if (parentPlatformOld != null && parentPlatformOld.isEnable() && !parentPlatform.isEnable()) { // 关闭启用时注销
-                try {
-                    commanderForPlatform.unregister(parentPlatformOld, null, null);
-                } catch (InvalidArgumentException | ParseException | SipException e) {
-                    logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
-                }
-                // 停止订阅相关的定时任务
-                subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId());
-            }
-        } else {
-            throw new ControllerException(ErrorCode.ERROR100.getCode(),"写入数据库失败");
-        }
+
+        platformService.update(parentPlatform);
     }
     }
 
 
     /**
     /**
@@ -279,12 +230,16 @@ public class PlatformController {
             throw new ControllerException(ErrorCode.ERROR400);
             throw new ControllerException(ErrorCode.ERROR400);
         }
         }
         ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId);
         ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId);
+        ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(serverGBId);
         if (parentPlatform == null) {
         if (parentPlatform == null) {
             throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在");
             throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在");
         }
         }
+        if (parentPlatformCatch == null) {
+            throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在");
+        }
         // 发送离线消息,无论是否成功都删除缓存
         // 发送离线消息,无论是否成功都删除缓存
         try {
         try {
-            commanderForPlatform.unregister(parentPlatform, (event -> {
+            commanderForPlatform.unregister(parentPlatform, parentPlatformCatch.getSipTransactionInfo(), (event -> {
                 // 清空redis缓存
                 // 清空redis缓存
                 redisCatchStorage.delPlatformCatchInfo(parentPlatform.getServerGBId());
                 redisCatchStorage.delPlatformCatchInfo(parentPlatform.getServerGBId());
                 redisCatchStorage.delPlatformKeepalive(parentPlatform.getServerGBId());
                 redisCatchStorage.delPlatformKeepalive(parentPlatform.getServerGBId());

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java

@@ -41,7 +41,7 @@ import java.util.UUID;
  * @author lin
  * @author lin
  */
  */
 @Tag(name  = "国标设备点播")
 @Tag(name  = "国标设备点播")
-@CrossOrigin
+
 @RestController
 @RestController
 @RequestMapping("/api/play")
 @RequestMapping("/api/play")
 public class PlayController {
 public class PlayController {

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java

@@ -40,7 +40,7 @@ import java.util.UUID;
  * @author lin
  * @author lin
  */
  */
 @Tag(name = "视频回放")
 @Tag(name = "视频回放")
-@CrossOrigin
+
 @RestController
 @RestController
 @RequestMapping("/api/playback")
 @RequestMapping("/api/playback")
 public class PlaybackController {
 public class PlaybackController {

+ 7 - 9
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java

@@ -1,7 +1,12 @@
 package com.genersoft.iot.vmp.vmanager.gb28181.ptz;
 package com.genersoft.iot.vmp.vmanager.gb28181.ptz;
 
 
- 
+
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.Parameter;
@@ -10,23 +15,16 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.util.ObjectUtils;
 import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.context.request.async.DeferredResult;
 import org.springframework.web.context.request.async.DeferredResult;
 
 
-import com.genersoft.iot.vmp.gb28181.bean.Device;
-import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
-import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
-import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
-
 import javax.sip.InvalidArgumentException;
 import javax.sip.InvalidArgumentException;
 import javax.sip.SipException;
 import javax.sip.SipException;
 import java.text.ParseException;
 import java.text.ParseException;
 import java.util.UUID;
 import java.util.UUID;
 
 
 @Tag(name  = "云台控制")
 @Tag(name  = "云台控制")
-@CrossOrigin
+
 @RestController
 @RestController
 @RequestMapping("/api/ptz")
 @RequestMapping("/api/ptz")
 public class PtzController {
 public class PtzController {

+ 8 - 11
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java

@@ -3,40 +3,37 @@ package com.genersoft.iot.vmp.vmanager.gb28181.record;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.conf.exception.ControllerException;
 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
+import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
 import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.service.IPlayService;
 import com.genersoft.iot.vmp.service.IPlayService;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
-
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.Parameter;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.CrossOrigin;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.context.request.async.DeferredResult;
 import org.springframework.web.context.request.async.DeferredResult;
 
 
-import com.genersoft.iot.vmp.gb28181.bean.Device;
-import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
-import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
-import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
-
 import javax.sip.InvalidArgumentException;
 import javax.sip.InvalidArgumentException;
 import javax.sip.SipException;
 import javax.sip.SipException;
 import java.text.ParseException;
 import java.text.ParseException;
 import java.util.UUID;
 import java.util.UUID;
 
 
 @Tag(name  = "国标录像")
 @Tag(name  = "国标录像")
-@CrossOrigin
+
 @RestController
 @RestController
 @RequestMapping("/api/gb_record")
 @RequestMapping("/api/gb_record")
 public class GBRecordController {
 public class GBRecordController {
@@ -74,10 +71,10 @@ public class GBRecordController {
 		}
 		}
 		DeferredResult<WVPResult<RecordInfo>> result = new DeferredResult<>();
 		DeferredResult<WVPResult<RecordInfo>> result = new DeferredResult<>();
 		if (!DateUtil.verification(startTime, DateUtil.formatter)){
 		if (!DateUtil.verification(startTime, DateUtil.formatter)){
-			throw new ControllerException(ErrorCode.ERROR100.getCode(), "startTime error, format is " + DateUtil.PATTERN);
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "startTime格式为" + DateUtil.PATTERN);
 		}
 		}
 		if (!DateUtil.verification(endTime, DateUtil.formatter)){
 		if (!DateUtil.verification(endTime, DateUtil.formatter)){
-			throw new ControllerException(ErrorCode.ERROR100.getCode(), "endTime error, format is " + DateUtil.PATTERN);
+			throw new ControllerException(ErrorCode.ERROR100.getCode(), "endTime格式为" + DateUtil.PATTERN);
 		}
 		}
 
 
 		Device device = storager.queryVideoDevice(deviceId);
 		Device device = storager.queryVideoDevice(deviceId);

+ 0 - 0
src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java


Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels