Browse Source

Merge branch 'wvp-28181-2.0'

# Conflicts:
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
#	src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
#	src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java
#	src/main/java/com/genersoft/iot/vmp/service/IMediaServerService.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
#	src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
#	web_src/src/components/dialog/devicePlayer.vue
648540858 3 năm trước cách đây
mục cha
commit
f223aad705
100 tập tin đã thay đổi với 2313 bổ sung1752 xóa
  1. 2 2
      README.md
  2. 1 1
      doc/_content/qa/play_error.md
  3. 10 6
      pom.xml
  4. 22 16
      sql/mysql.sql
  5. 33 0
      sql/update.sql
  6. 269 151
      src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
  7. 80 0
      src/main/java/com/genersoft/iot/vmp/common/StreamURL.java
  8. 54 0
      src/main/java/com/genersoft/iot/vmp/common/SystemAllInfo.java
  9. 0 22
      src/main/java/com/genersoft/iot/vmp/common/SystemInfoDto.java
  10. 20 7
      src/main/java/com/genersoft/iot/vmp/common/VersionPo.java
  11. 3 3
      src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java
  12. 10 7
      src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java
  13. 2 2
      src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java
  14. 3 14
      src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java
  15. 0 1
      src/main/java/com/genersoft/iot/vmp/conf/MediaStatusTimerTask.java
  16. 7 22
      src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
  17. 1 2
      src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java
  18. 5 2
      src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java
  19. 30 0
      src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
  20. 1 0
      src/main/java/com/genersoft/iot/vmp/conf/VersionInfo.java
  21. 1 1
      src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java
  22. 1 3
      src/main/java/com/genersoft/iot/vmp/conf/redis/RedisConfig.java
  23. 1 1
      src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java
  24. 105 56
      src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java
  25. 32 0
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
  26. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamInfo.java
  27. 1 3
      src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeInfo.java
  28. 1 0
      src/main/java/com/genersoft/iot/vmp/gb28181/conf/SipLoggerPass.java
  29. 5 2
      src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java
  30. 5 9
      src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java
  31. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java
  32. 0 51
      src/main/java/com/genersoft/iot/vmp/gb28181/task/SipDeviceRunner.java
  33. 95 0
      src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java
  34. 3 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java
  35. 132 0
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java
  36. 52 23
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
  37. 2 5
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
  38. 74 73
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
  39. 121 133
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
  40. 131 192
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
  41. 37 89
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
  42. 73 76
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
  43. 21 6
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java
  44. 2 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java
  45. 108 97
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
  46. 3 4
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
  47. 29 19
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
  48. 20 28
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
  49. 7 9
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/info/InfoRequestProcessor.java
  50. 5 12
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/MessageRequestProcessor.java
  51. 24 24
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
  52. 6 4
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
  53. 2 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
  54. 2 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MediaStatusNotifyMessageHandler.java
  55. 4 3
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java
  56. 2 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/AlarmQueryMessageHandler.java
  57. 2 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/CatalogQueryMessageHandler.java
  58. 2 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java
  59. 2 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceStatusQueryMessageHandler.java
  60. 7 9
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java
  61. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/AlarmResponseMessageHandler.java
  62. 18 4
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java
  63. 2 3
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
  64. 3 2
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/ConfigDownloadResponseMessageHandler.java
  65. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceConfigResponseMessageHandler.java
  66. 3 2
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java
  67. 5 17
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceInfoResponseMessageHandler.java
  68. 3 2
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java
  69. 5 7
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java
  70. 5 5
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/PresetQueryResponseMessageHandler.java
  71. 2 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
  72. 8 21
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/InviteResponseProcessor.java
  73. 1 1
      src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java
  74. 10 4
      src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java
  75. 6 2
      src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java
  76. 2 2
      src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java
  77. 265 377
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
  78. 7 6
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java
  79. 2 3
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
  80. 64 37
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
  81. 21 11
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java
  82. 4 4
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java
  83. 1 1
      src/main/java/com/genersoft/iot/vmp/media/zlm/ZlmHttpHookSubscribe.java
  84. 13 2
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java
  85. 44 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForRtpServerTimeout.java
  86. 2 2
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForServerStarted.java
  87. 3 2
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamChange.java
  88. 2 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java
  89. 1 1
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IHookSubscribe.java
  90. 3 16
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java
  91. 4 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ServerKeepaliveData.java
  92. 9 6
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamAuthorityInfo.java
  93. 7 7
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java
  94. 1 1
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookParam.java
  95. 5 1
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/OnPlayHookParam.java
  96. 5 1
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/OnPublishHookParam.java
  97. 53 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnRtpServerTimeoutHookParam.java
  98. 27 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnSendRtpStoppedHookParam.java
  99. 20 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/OnServerKeepaliveHookParam.java
  100. 0 0
      src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java

+ 2 - 2
README.md

@@ -101,6 +101,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
     - [X] 目录订阅与通知
     - [X] 录像查看与播放
     - [X] GPS订阅与通知(直播推流)
+- [X] 支持手动添加设备和给设备设置单独的密码
 - [X] 添加RTSP视频
 - [X] 添加接口鉴权
 - [X] 添加RTMP视频
@@ -155,7 +156,6 @@ QQ私信一般不回, 精力有限.欢迎大家在群里讨论.觉得项目对
 
 # 授权协议
 本项目自有代码使用宽松的MIT协议,在保留版权信息的情况下可以自由应用于各自商用、非商业的项目。 但是本项目也零碎的使用了一些其他的开源代码,在商用的情况下请自行替代或剔除; 由于使用本项目而产生的商业纠纷或侵权行为一概与本项目及开发者无关,请自行承担法律风险。 在使用本项目代码时,也应该在授权协议中同时表明本项目依赖的第三方库的协议
-
 # 致谢
 感谢作者[夏楚](https://github.com/xia-chu) 提供这么棒的开源流媒体服务框架,并在开发过程中给予支持与帮助。     
 感谢作者[dexter langhuihui](https://github.com/langhuihui) 开源这么好用的WEB播放器。     
@@ -165,7 +165,7 @@ QQ私信一般不回, 精力有限.欢迎大家在群里讨论.觉得项目对
 [hotcoffie](https://github.com/hotcoffie) [xiaomu](https://github.com/nikmu) [TristingChen](https://github.com/TristingChen)
 [chenparty](https://github.com/chenparty) [Hotleave](https://github.com/hotleave) [ydwxb](https://github.com/ydwxb)
 [ydpd](https://github.com/ydpd) [szy833](https://github.com/szy833) [ydwxb](https://github.com/ydwxb) [Albertzhu666](https://github.com/Albertzhu666)
-[mk1990](https://github.com/mk1990)
+[mk1990](https://github.com/mk1990) [SaltFish001](https://github.com/SaltFish001)
 
 ps: 刚增加了这个名单,肯定遗漏了一些大佬,欢迎大佬联系我添加。
 

+ 1 - 1
doc/_content/qa/play_error.md

@@ -1,6 +1,6 @@
 <!-- 点播错误 -->
 # 点播错误
-排查点播错误你首先要清[点播的基本流程](_content/theory/play.md),一般的流程如下:
+排查点播错误你首先要清[点播的基本流程](_content/theory/play.md),一般的流程如下:
 ```plantuml
 @startuml
 "WEB用户"  -> "WVP-PRO": 1. 发起点播请求

+ 10 - 6
pom.xml

@@ -11,7 +11,7 @@
 
 	<groupId>com.genersoft</groupId>
 	<artifactId>wvp-pro</artifactId>
-	<version>2.3.2</version>
+	<version>2.6.6</version>
 	<name>web video platform</name>
 	<description>国标28181视频平台</description>
 
@@ -153,13 +153,17 @@
 			<version>2.1.3</version>
 		</dependency>
 
-		<!-- json解析库fastjson -->
+		<!-- json解析库fastjson2 -->
 		<dependency>
-			<groupId>com.alibaba</groupId>
-			<artifactId>fastjson</artifactId>
-			<version>1.2.83</version>
+			<groupId>com.alibaba.fastjson2</groupId>
+			<artifactId>fastjson2</artifactId>
+			<version>2.0.17</version>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba.fastjson2</groupId>
+			<artifactId>fastjson2-extension</artifactId>
+			<version>2.0.17</version>
 		</dependency>
-
 
 		<!-- okhttp -->
 		<dependency>

+ 22 - 16
sql/mysql.sql

@@ -1,8 +1,8 @@
+-- MySQL dump 10.13  Distrib 8.0.30, for Linux (x86_64)
 --
+-- Host: 127.0.0.1    Database: wvp
 -- ------------------------------------------------------
+-- Server version	8.0.30
 
 /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
 /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
@@ -34,13 +34,13 @@ CREATE TABLE `device` (
                           `online` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `registerTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `keepaliveTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
-                          `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                          `ip` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                           `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
-                          `port` int NOT NULL,
-                          `expires` int NOT NULL,
-                          `subscribeCycleForCatalog` int NOT NULL,
-                          `hostAddress` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+                          `port` int DEFAULT NULL,
+                          `expires` int DEFAULT NULL,
+                          `subscribeCycleForCatalog` int DEFAULT NULL,
+                          `hostAddress` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
                           `charset` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                           `subscribeCycleForMobilePosition` int DEFAULT NULL,
                           `mobilePositionSubmissionInterval` int DEFAULT '5',
@@ -49,9 +49,11 @@ CREATE TABLE `device` (
                           `geoCoordSys` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                           `treeType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                           `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'auto',
+                          `custom_name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
+                          `password` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL,
                           PRIMARY KEY (`id`),
                           UNIQUE KEY `device_deviceId_uindex` (`deviceId`)
-) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=47 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
 /*!40101 SET character_set_client = @saved_cs_client */;
 
 --
@@ -143,7 +145,7 @@ CREATE TABLE `device_channel` (
                                   PRIMARY KEY (`id`),
                                   UNIQUE KEY `device_channel_id_uindex` (`id`),
                                   UNIQUE KEY `device_channel_pk` (`channelId`,`deviceId`)
-) ENGINE=InnoDB AUTO_INCREMENT=60163 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=60301 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
 /*!40101 SET character_set_client = @saved_cs_client */;
 
 --
@@ -213,7 +215,7 @@ CREATE TABLE `gb_stream` (
                              PRIMARY KEY (`gbStreamId`) USING BTREE,
                              UNIQUE KEY `app` (`app`,`stream`) USING BTREE,
                              UNIQUE KEY `gbId` (`gbId`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=301057 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
+) ENGINE=InnoDB AUTO_INCREMENT=301059 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
 /*!40101 SET character_set_client = @saved_cs_client */;
 
 --
@@ -243,7 +245,7 @@ CREATE TABLE `log` (
                        `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                        `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                        PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=727574 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
+) ENGINE=InnoDB AUTO_INCREMENT=733627 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
 /*!40101 SET character_set_client = @saved_cs_client */;
 
 --
@@ -279,7 +281,6 @@ CREATE TABLE `media_server` (
                                 `secret` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                                 `rtpEnable` int NOT NULL,
                                 `rtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
-                                `sendRtpPortRange` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                                 `recordAssistPort` int NOT NULL,
                                 `defaultServer` int NOT NULL,
                                 `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
@@ -336,7 +337,7 @@ CREATE TABLE `parent_platform` (
                                    PRIMARY KEY (`id`),
                                    UNIQUE KEY `parent_platform_id_uindex` (`id`),
                                    UNIQUE KEY `parent_platform_pk` (`serverGBId`)
-) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
+) ENGINE=InnoDB AUTO_INCREMENT=44 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
 /*!40101 SET character_set_client = @saved_cs_client */;
 
 --
@@ -388,7 +389,7 @@ CREATE TABLE `platform_gb_channel` (
                                        `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                                        `deviceChannelId` int NOT NULL,
                                        PRIMARY KEY (`id`)
-) ENGINE=InnoDB AUTO_INCREMENT=51 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=102 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
 /*!40101 SET character_set_client = @saved_cs_client */;
 
 --
@@ -414,7 +415,7 @@ CREATE TABLE `platform_gb_stream` (
                                       `id` int NOT NULL AUTO_INCREMENT,
                                       PRIMARY KEY (`id`),
                                       UNIQUE KEY `platform_gb_stream_pk` (`platformId`,`catalogId`,`gbStreamId`)
-) ENGINE=InnoDB AUTO_INCREMENT=301759 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
+) ENGINE=InnoDB AUTO_INCREMENT=301766 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
 /*!40101 SET character_set_client = @saved_cs_client */;
 
 --
@@ -453,9 +454,10 @@ CREATE TABLE `stream_proxy` (
                                 `createTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
                                 `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                                 `updateTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+                                `enable_disable_none_reader` bit(1) DEFAULT NULL,
                                 PRIMARY KEY (`id`),
                                 UNIQUE KEY `stream_proxy_pk` (`app`,`stream`)
-) ENGINE=InnoDB AUTO_INCREMENT=545 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=548 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
 /*!40101 SET character_set_client = @saved_cs_client */;
 
 --
@@ -492,7 +494,7 @@ CREATE TABLE `stream_push` (
                                `self` int DEFAULT NULL,
                                PRIMARY KEY (`id`),
                                UNIQUE KEY `stream_push_pk` (`app`,`stream`)
-) ENGINE=InnoDB AUTO_INCREMENT=310546 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+) ENGINE=InnoDB AUTO_INCREMENT=310558 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
 /*!40101 SET character_set_client = @saved_cs_client */;
 
 --
@@ -521,7 +523,7 @@ CREATE TABLE `user` (
                         `pushKey` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
                         PRIMARY KEY (`id`) USING BTREE,
                         UNIQUE KEY `user_username_uindex` (`username`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
+) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;
 /*!40101 SET character_set_client = @saved_cs_client */;
 
 --
@@ -570,4 +572,4 @@ UNLOCK TABLES;
 /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
 /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
 
+-- Dump completed on 2022-10-18 17:00:02

+ 33 - 0
sql/update.sql

@@ -1,5 +1,38 @@
 alter table media_server
     drop column streamNoneReaderDelayMS;
 
+alter table media_server
+    drop column sendRtpPortRange;
+
 alter table stream_proxy
     add enable_disable_none_reader bit(1) default null;
+
+alter table device
+    add mediaServerId varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'auto';
+
+alter table device
+    add custom_name varchar(255) default null;
+
+alter table device
+    add sdpIp varchar(50) default null;
+
+alter table device
+    add localIp varchar(50) default null;
+
+alter table device
+    add password varchar(255) default null;
+
+alter table device
+    modify ip varchar(50) null;
+
+alter table device
+    modify port int null;
+
+alter table device
+    modify expires int null;
+
+alter table device
+    modify subscribeCycleForCatalog int null;
+
+alter table device
+    modify hostAddress varchar(50) null;

+ 269 - 151
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java

@@ -2,8 +2,10 @@ package com.genersoft.iot.vmp.common;
 
 import io.swagger.v3.oas.annotations.media.Schema;
 
+import java.io.Serializable;
+
 @Schema(description = "流信息")
-public class StreamInfo {
+public class StreamInfo implements Serializable, Cloneable{
 
     @Schema(description = "应用名")
     private String app;
@@ -13,54 +15,56 @@ public class StreamInfo {
     private String deviceID;
     @Schema(description = "通道编号")
     private String channelId;
-    @Schema(description = "HTTP-FLV流地址")
-    private String flv;
 
     @Schema(description = "IP")
     private String ip;
+
+    @Schema(description = "HTTP-FLV流地址")
+    private StreamURL flv;
+
     @Schema(description = "HTTPS-FLV流地址")
-    private String https_flv;
+    private StreamURL https_flv;
     @Schema(description = "Websocket-FLV流地址")
-    private String ws_flv;
+    private StreamURL ws_flv;
     @Schema(description = "Websockets-FLV流地址")
-    private String wss_flv;
+    private StreamURL wss_flv;
     @Schema(description = "HTTP-FMP4流地址")
-    private String fmp4;
+    private StreamURL fmp4;
     @Schema(description = "HTTPS-FMP4流地址")
-    private String https_fmp4;
+    private StreamURL https_fmp4;
     @Schema(description = "Websocket-FMP4流地址")
-    private String ws_fmp4;
+    private StreamURL ws_fmp4;
     @Schema(description = "Websockets-FMP4流地址")
-    private String wss_fmp4;
+    private StreamURL wss_fmp4;
     @Schema(description = "HLS流地址")
-    private String hls;
+    private StreamURL hls;
     @Schema(description = "HTTPS-HLS流地址")
-    private String https_hls;
+    private StreamURL https_hls;
     @Schema(description = "Websocket-HLS流地址")
-    private String ws_hls;
+    private StreamURL ws_hls;
     @Schema(description = "Websockets-HLS流地址")
-    private String wss_hls;
+    private StreamURL wss_hls;
     @Schema(description = "HTTP-TS流地址")
-    private String ts;
+    private StreamURL ts;
     @Schema(description = "HTTPS-TS流地址")
-    private String https_ts;
+    private StreamURL https_ts;
     @Schema(description = "Websocket-TS流地址")
-    private String ws_ts;
+    private StreamURL ws_ts;
     @Schema(description = "Websockets-TS流地址")
-    private String wss_ts;
+    private StreamURL wss_ts;
     @Schema(description = "RTMP流地址")
-    private String rtmp;
+    private StreamURL rtmp;
     @Schema(description = "RTMPS流地址")
-    private String rtmps;
+    private StreamURL rtmps;
     @Schema(description = "RTSP流地址")
-    private String rtsp;
+    private StreamURL rtsp;
     @Schema(description = "RTSPS流地址")
-    private String rtsps;
+    private StreamURL rtsps;
     @Schema(description = "RTC流地址")
-    private String rtc;
+    private StreamURL rtc;
 
     @Schema(description = "RTCS流地址")
-    private String rtcs;
+    private StreamURL rtcs;
     @Schema(description = "流媒体ID")
     private String mediaServerId;
     @Schema(description = "流编码信息")
@@ -75,125 +79,221 @@ public class StreamInfo {
     @Schema(description = "是否暂停(录像回放使用)")
     private boolean pause;
 
-    public static class TransactionInfo{
-        public String callId;
-        public String localTag;
-        public String remoteTag;
-        public String branch;
+    public void setFlv(StreamURL flv) {
+        this.flv = flv;
     }
 
-    private TransactionInfo transactionInfo;
+    public void setHttps_flv(StreamURL https_flv) {
+        this.https_flv = https_flv;
+    }
 
-    public String getApp() {
-        return app;
+    public void setWs_flv(StreamURL ws_flv) {
+        this.ws_flv = ws_flv;
     }
 
-    public void setApp(String app) {
-        this.app = app;
+    public void setWss_flv(StreamURL wss_flv) {
+        this.wss_flv = wss_flv;
     }
 
-    public String getDeviceID() {
-        return deviceID;
+    public void setFmp4(StreamURL fmp4) {
+        this.fmp4 = fmp4;
     }
 
-    public void setDeviceID(String deviceID) {
-        this.deviceID = deviceID;
+    public void setHttps_fmp4(StreamURL https_fmp4) {
+        this.https_fmp4 = https_fmp4;
     }
 
-    public String getChannelId() {
-        return channelId;
+    public void setWs_fmp4(StreamURL ws_fmp4) {
+        this.ws_fmp4 = ws_fmp4;
     }
 
-    public void setChannelId(String channelId) {
-        this.channelId = channelId;
+    public void setWss_fmp4(StreamURL wss_fmp4) {
+        this.wss_fmp4 = wss_fmp4;
     }
 
-    public String getFlv() {
-        return flv;
+    public void setHls(StreamURL hls) {
+        this.hls = hls;
     }
 
-    public void setFlv(String flv) {
-        this.flv = flv;
+    public void setHttps_hls(StreamURL https_hls) {
+        this.https_hls = https_hls;
     }
 
-    public String getWs_flv() {
-        return ws_flv;
+    public void setWs_hls(StreamURL ws_hls) {
+        this.ws_hls = ws_hls;
     }
 
-    public void setWs_flv(String ws_flv) {
-        this.ws_flv = ws_flv;
+    public void setWss_hls(StreamURL wss_hls) {
+        this.wss_hls = wss_hls;
     }
 
-    public String getRtmp() {
-        return rtmp;
+    public void setTs(StreamURL ts) {
+        this.ts = ts;
     }
 
-    public void setRtmp(String rtmp) {
-        this.rtmp = rtmp;
+    public void setHttps_ts(StreamURL https_ts) {
+        this.https_ts = https_ts;
     }
 
-    public String getHls() {
-        return hls;
+    public void setWs_ts(StreamURL ws_ts) {
+        this.ws_ts = ws_ts;
     }
 
-    public void setHls(String hls) {
-        this.hls = hls;
+    public void setWss_ts(StreamURL wss_ts) {
+        this.wss_ts = wss_ts;
     }
 
-    public String getRtsp() {
-        return rtsp;
+    public void setRtmp(StreamURL rtmp) {
+        this.rtmp = rtmp;
     }
 
-    public void setRtsp(String rtsp) {
-        this.rtsp = rtsp;
+    public void setRtmps(StreamURL rtmps) {
+        this.rtmps = rtmps;
     }
 
-    public Object getTracks() {
-        return tracks;
+    public void setRtsp(StreamURL rtsp) {
+        this.rtsp = rtsp;
     }
 
-    public void setTracks(Object tracks) {
-        this.tracks = tracks;
+    public void setRtsps(StreamURL rtsps) {
+        this.rtsps = rtsps;
     }
 
-    public String getFmp4() {
-        return fmp4;
+    public void setRtc(StreamURL rtc) {
+        this.rtc = rtc;
     }
 
-    public void setFmp4(String fmp4) {
-        this.fmp4 = fmp4;
+    public void setRtcs(StreamURL rtcs) {
+        this.rtcs = rtcs;
     }
 
-    public String getWs_fmp4() {
-        return ws_fmp4;
+    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);
+        this.rtmp = new StreamURL("rtmp", host, port, file);
+        if (sslPort != 0) {
+            this.rtmps = new StreamURL("rtmps", host, sslPort, file);
+        }
+    }
+
+    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);
+        this.rtsp = new StreamURL("rtsp", host, port, file);
+        if (sslPort != 0) {
+            this.rtsps = new StreamURL("rtsps", host, sslPort, file);
+        }
+    }
+
+    public void setFlv(String host, int port, int sslPort, String app, String stream, String callIdParam) {
+        String file = String.format("%s/%s.live.flv%s", app, stream, callIdParam);
+        this.flv = new StreamURL("http", host, port, file);
+        this.ws_flv = new StreamURL("ws", host, port, file);
+        if (sslPort != 0) {
+            this.https_flv = new StreamURL("https", host, sslPort, file);
+            this.wss_flv = new StreamURL("wss", host, sslPort, file);
+        }
+    }
+
+    public void setFmp4(String host, int port, int sslPort, String app, String stream, String callIdParam) {
+        String file = String.format("%s/%s.live.mp4%s", app, stream, callIdParam);
+        this.fmp4 = new StreamURL("http", host, port, file);
+        this.ws_fmp4 = new StreamURL("ws", host, port, file);
+        if (sslPort != 0) {
+            this.https_fmp4 = new StreamURL("https", host, sslPort, file);
+            this.wss_fmp4 = new StreamURL("wss", host, sslPort, file);
+        }
+    }
+
+    public void setHls(String host, int port, int sslPort, String app, String stream, String callIdParam) {
+        String file = String.format("%s/%s/hls.m3u8%s", app, stream, callIdParam);
+        this.hls = new StreamURL("http", host, port, file);
+        this.ws_hls = new StreamURL("ws", host, port, file);
+        if (sslPort != 0) {
+            this.https_hls = new StreamURL("https", host, sslPort, file);
+            this.wss_hls = new StreamURL("wss", host, sslPort, file);
+        }
+    }
+
+    public void setTs(String host, int port, int sslPort, String app, String stream, String callIdParam) {
+        String file = String.format("%s/%s.live.ts%s", app, stream, callIdParam);
+        this.ts = new StreamURL("http", host, port, file);
+        this.ws_ts = new StreamURL("ws", host, port, file);
+        if (sslPort != 0) {
+            this.https_ts = new StreamURL("https", host, sslPort, file);
+            this.wss_ts = new StreamURL("wss", host, sslPort, file);
+        }
+    }
+
+    public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam) {
+        String file = String.format("index/api/webrtc?app=%s&stream=%s&type=play%s", app, stream, callIdParam);
+        this.rtc = new StreamURL("http", host, port, file);
+        if (sslPort != 0) {
+            this.rtcs = new StreamURL("https", host, sslPort, file);
+        }
+    }
+
+    public void channgeStreamIp(String localAddr) {
+        this.flv.setHost(localAddr);
+        this.ws_flv.setHost(localAddr);
+        this.hls.setHost(localAddr);
+        this.ws_hls.setHost(localAddr);
+        this.ts.setHost(localAddr);
+        this.ws_ts.setHost(localAddr);
+        this.fmp4.setHost(localAddr);
+        this.ws_fmp4.setHost(localAddr);
+        this.rtc.setHost(localAddr);
+        if (this.https_flv != null) {
+            this.https_flv.setHost(localAddr);
+            this.wss_flv.setHost(localAddr);
+            this.https_hls.setHost(localAddr);
+            this.wss_hls.setHost(localAddr);
+            this.wss_ts.setHost(localAddr);
+            this.https_fmp4.setHost(localAddr);
+            this.wss_fmp4.setHost(localAddr);
+            this.rtcs.setHost(localAddr);
+        }
+        this.rtsp.setHost(localAddr);
+        if (this.rtsps != null) {
+            this.rtsps.setHost(localAddr);
+        }
+        this.rtmp.setHost(localAddr);
+        if (this.rtmps != null) {
+            this.rtmps.setHost(localAddr);
+        }
+
     }
 
-    public void setWs_fmp4(String ws_fmp4) {
-        this.ws_fmp4 = ws_fmp4;
+
+    public static class TransactionInfo{
+        public String callId;
+        public String localTag;
+        public String remoteTag;
+        public String branch;
     }
 
-    public String getWs_hls() {
-        return ws_hls;
+    private TransactionInfo transactionInfo;
+
+    public String getApp() {
+        return app;
     }
 
-    public void setWs_hls(String ws_hls) {
-        this.ws_hls = ws_hls;
+    public void setApp(String app) {
+        this.app = app;
     }
 
-    public String getTs() {
-        return ts;
+    public String getDeviceID() {
+        return deviceID;
     }
 
-    public void setTs(String ts) {
-        this.ts = ts;
+    public void setDeviceID(String deviceID) {
+        this.deviceID = deviceID;
     }
 
-    public String getWs_ts() {
-        return ws_ts;
+    public String getChannelId() {
+        return channelId;
     }
 
-    public void setWs_ts(String ws_ts) {
-        this.ws_ts = ws_ts;
+    public void setChannelId(String channelId) {
+        this.channelId = channelId;
     }
 
     public String getStream() {
@@ -204,110 +304,125 @@ public class StreamInfo {
         this.stream = stream;
     }
 
-    public String getRtc() {
-        return rtc;
+    public String getIp() {
+        return ip;
     }
 
-    public void setRtc(String rtc) {
-        this.rtc = rtc;
+    public void setIp(String ip) {
+        this.ip = ip;
     }
 
-    public TransactionInfo getTransactionInfo() {
-        return transactionInfo;
+    public StreamURL getFlv() {
+        return flv;
     }
 
-    public void setTransactionInfo(TransactionInfo transactionInfo) {
-        this.transactionInfo = transactionInfo;
+    public StreamURL getHttps_flv() {
+        return https_flv;
     }
 
-    public String getMediaServerId() {
-        return mediaServerId;
+    public StreamURL getWs_flv() {
+        return ws_flv;
     }
 
-    public void setMediaServerId(String mediaServerId) {
-        this.mediaServerId = mediaServerId;
-    }
 
-    public String getHttps_flv() {
-        return https_flv;
+    public StreamURL getWss_flv() {
+        return wss_flv;
     }
 
-    public void setHttps_flv(String https_flv) {
-        this.https_flv = https_flv;
+    public StreamURL getFmp4() {
+        return fmp4;
     }
 
-    public String getWss_flv() {
-        return wss_flv;
+
+
+    public StreamURL getHttps_fmp4() {
+        return https_fmp4;
     }
 
-    public void setWss_flv(String wss_flv) {
-        this.wss_flv = wss_flv;
+    public StreamURL getWs_fmp4() {
+        return ws_fmp4;
     }
 
-    public String getWss_fmp4() {
+    public StreamURL getWss_fmp4() {
         return wss_fmp4;
     }
 
-    public void setWss_fmp4(String wss_fmp4) {
-        this.wss_fmp4 = wss_fmp4;
+    public StreamURL getHls() {
+        return hls;
     }
 
-    public String getWss_hls() {
+
+    public StreamURL getHttps_hls() {
+        return https_hls;
+    }
+
+    public StreamURL getWs_hls() {
+        return ws_hls;
+    }
+
+    public StreamURL getWss_hls() {
         return wss_hls;
     }
 
-    public void setWss_hls(String wss_hls) {
-        this.wss_hls = wss_hls;
+    public StreamURL getTs() {
+        return ts;
     }
 
-    public String getWss_ts() {
-        return wss_ts;
+
+    public StreamURL getHttps_ts() {
+        return https_ts;
     }
 
-    public void setWss_ts(String wss_ts) {
-        this.wss_ts = wss_ts;
+
+    public StreamURL getWs_ts() {
+        return ws_ts;
     }
 
-    public String getRtmps() {
-        return rtmps;
+
+    public StreamURL getWss_ts() {
+        return wss_ts;
     }
 
-    public void setRtmps(String rtmps) {
-        this.rtmps = rtmps;
+
+    public StreamURL getRtmp() {
+        return rtmp;
     }
 
-    public String getRtsps() {
-        return rtsps;
+    public StreamURL getRtmps() {
+        return rtmps;
     }
 
-    public void setRtsps(String rtsps) {
-        this.rtsps = rtsps;
+    public StreamURL getRtsp() {
+        return rtsp;
     }
 
-    public String getHttps_hls() {
-        return https_hls;
+    public StreamURL getRtsps() {
+        return rtsps;
     }
 
-    public void setHttps_hls(String https_hls) {
-        this.https_hls = https_hls;
+    public StreamURL getRtc() {
+        return rtc;
     }
 
-    public String getHttps_fmp4() {
-        return https_fmp4;
+    public StreamURL getRtcs() {
+        return rtcs;
     }
 
-    public void setHttps_fmp4(String https_fmp4) {
-        this.https_fmp4 = https_fmp4;
+    public String getMediaServerId() {
+        return mediaServerId;
     }
 
-    public String getHttps_ts() {
-        return https_ts;
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
     }
 
-    public void setHttps_ts(String https_ts) {
-        this.https_ts = https_ts;
+    public Object getTracks() {
+        return tracks;
     }
 
+    public void setTracks(Object tracks) {
+        this.tracks = tracks;
+    }
 
     public String getStartTime() {
         return startTime;
@@ -333,27 +448,30 @@ public class StreamInfo {
         this.progress = progress;
     }
 
-    public String getIp() {
-        return ip;
-    }
-
-    public void setIp(String ip) {
-        this.ip = ip;
+    public boolean isPause() {
+        return pause;
     }
 
-    public String getRtcs() {
-        return rtcs;
+    public void setPause(boolean pause) {
+        this.pause = pause;
     }
 
-    public void setRtcs(String rtcs) {
-        this.rtcs = rtcs;
+    public TransactionInfo getTransactionInfo() {
+        return transactionInfo;
     }
 
-    public boolean isPause() {
-        return pause;
+    public void setTransactionInfo(TransactionInfo transactionInfo) {
+        this.transactionInfo = transactionInfo;
     }
 
-    public void setPause(boolean pause) {
-        this.pause = pause;
+    @Override
+    public StreamInfo clone() {
+        StreamInfo instance = null;
+        try{
+            instance = (StreamInfo)super.clone();
+        }catch(CloneNotSupportedException e) {
+            e.printStackTrace();
+        }
+        return instance;
     }
 }

+ 80 - 0
src/main/java/com/genersoft/iot/vmp/common/StreamURL.java

@@ -0,0 +1,80 @@
+package com.genersoft.iot.vmp.common;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import java.io.Serializable;
+
+
+@Schema(description = "流地址信息")
+public class StreamURL implements Serializable {
+
+    @Schema(description = "协议")
+    private String protocol;
+
+    @Schema(description = "主机地址")
+    private String host;
+
+    @Schema(description = "端口")
+    private int port = -1;
+
+    @Schema(description = "定位位置")
+    private String file;
+
+    @Schema(description = "拼接后的地址")
+    private String url;
+
+    public StreamURL() {
+    }
+
+    public StreamURL(String protocol, String host, int port, String file) {
+        this.protocol = protocol;
+        this.host = host;
+        this.port = port;
+        this.file = file;
+    }
+
+    public String getProtocol() {
+        return protocol;
+    }
+
+    public void setProtocol(String protocol) {
+        this.protocol = protocol;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public String getFile() {
+        return file;
+    }
+
+    public void setFile(String file) {
+        this.file = file;
+    }
+
+    public String getUrl() {
+        return this.toString();
+    }
+
+    @Override
+    public String toString() {
+        if (protocol != null && host != null && port != -1 ) {
+            return String.format("%s://%s:%s/%s", protocol, host, port, file);
+        }else {
+            return null;
+        }
+    }
+}

+ 54 - 0
src/main/java/com/genersoft/iot/vmp/common/SystemAllInfo.java

@@ -0,0 +1,54 @@
+package com.genersoft.iot.vmp.common;
+
+import java.util.List;
+
+public class SystemAllInfo {
+
+    private List<Object> cpu;
+    private List<Object> mem;
+    private List<Object> net;
+
+    private long netTotal;
+
+    private Object disk;
+
+    public List<Object> getCpu() {
+        return cpu;
+    }
+
+    public void setCpu(List<Object> cpu) {
+        this.cpu = cpu;
+    }
+
+    public List<Object> getMem() {
+        return mem;
+    }
+
+    public void setMem(List<Object> mem) {
+        this.mem = mem;
+    }
+
+    public List<Object> getNet() {
+        return net;
+    }
+
+    public void setNet(List<Object> net) {
+        this.net = net;
+    }
+
+    public Object getDisk() {
+        return disk;
+    }
+
+    public void setDisk(Object disk) {
+        this.disk = disk;
+    }
+
+    public long getNetTotal() {
+        return netTotal;
+    }
+
+    public void setNetTotal(long netTotal) {
+        this.netTotal = netTotal;
+    }
+}

+ 0 - 22
src/main/java/com/genersoft/iot/vmp/common/SystemInfoDto.java

@@ -1,22 +0,0 @@
-package com.genersoft.iot.vmp.common;
-
-public class SystemInfoDto<T> {
-    private String time;
-    private T data;
-
-    public String getTime() {
-        return time;
-    }
-
-    public void setTime(String time) {
-        this.time = time;
-    }
-
-    public T getData() {
-        return data;
-    }
-
-    public void setData(T data) {
-        this.data = data;
-    }
-}

+ 20 - 7
src/main/java/com/genersoft/iot/vmp/common/VersionPo.java

@@ -1,33 +1,38 @@
 package com.genersoft.iot.vmp.common;
 
-import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson2.annotation.JSONField;
 
 public class VersionPo {
     /**
      * git的全版本号
      */
-    @JSONField(name="GIT-Revision")
+    @JSONField(name="GIT_Revision")
     private String GIT_Revision;
     /**
      * maven版本
      */
-    @JSONField(name = "Create-By")
+    @JSONField(name = "Create_By")
     private String Create_By;
     /**
      * git的分支
      */
-    @JSONField(name = "GIT-BRANCH")
+    @JSONField(name = "GIT_BRANCH")
     private String GIT_BRANCH;
     /**
      * git的url
      */
-    @JSONField(name = "GIT-URL")
+    @JSONField(name = "GIT_URL")
     private String GIT_URL;
     /**
      * 构建日期
      */
-    @JSONField(name = "BUILD-DATE")
+    @JSONField(name = "BUILD_DATE")
     private String BUILD_DATE;
+    /**
+     * 构建日期
+     */
+    @JSONField(name = "GIT_DATE")
+    private String GIT_DATE;
     /**
      * 项目名称 配合pom使用
      */
@@ -36,7 +41,7 @@ public class VersionPo {
     /**
      * git局部版本号
      */
-    @JSONField(name = "GIT-Revision-SHORT")
+    @JSONField(name = "GIT_Revision_SHORT")
     private String GIT_Revision_SHORT;
     /**
      * 项目的版本如2.0.1.0 配合pom使用
@@ -133,4 +138,12 @@ public class VersionPo {
     public String getBuild_Jdk() {
         return Build_Jdk;
     }
+
+    public String getGIT_DATE() {
+        return GIT_DATE;
+    }
+
+    public void setGIT_DATE(String GIT_DATE) {
+        this.GIT_DATE = GIT_DATE;
+    }
 }

+ 3 - 3
src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java

@@ -27,11 +27,9 @@ public class VideoManagerConstants {
 
 	public static final String KEEPLIVEKEY_PREFIX = "VMP_KEEPALIVE_";
 
-	// 此处多了一个_,暂不修改
+	// TODO 此处多了一个_,暂不修改
 	public static final String PLAYER_PREFIX = "VMP_PLAYER_";
 	public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_";
-	public static final String PLAY_INFO_PREFIX = "VMP_PLAY_INFO_";
-
 	public static final String DOWNLOAD_PREFIX = "VMP_DOWNLOAD_";
 
 	public static final String PLATFORM_KEEPALIVE_PREFIX = "VMP_PLATFORM_KEEPALIVE_";
@@ -70,6 +68,8 @@ public class VideoManagerConstants {
 
 	public static final String SYSTEM_INFO_NET_PREFIX = "VMP_SYSTEM_INFO_NET_";
 
+	public static final String SYSTEM_INFO_DISK_PREFIX = "VMP_SYSTEM_INFO_DISK_";
+
 
 
 

+ 10 - 7
src/main/java/com/genersoft/iot/vmp/conf/DynamicTask.java

@@ -1,10 +1,7 @@
 package com.genersoft.iot.vmp.conf;
 
-import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
 import org.springframework.stereotype.Component;
@@ -101,12 +98,14 @@ public class DynamicTask {
         }
     }
 
-    public void stop(String key) {
-        if (futureMap.get(key) != null && !futureMap.get(key).isCancelled()) {
-            futureMap.get(key).cancel(false);
+    public boolean stop(String key) {
+        boolean result = false;
+        if (futureMap.get(key) != null && !futureMap.get(key).isCancelled() && !futureMap.get(key).isDone()) {
+            result = futureMap.get(key).cancel(false);
             futureMap.remove(key);
             runnableMap.remove(key);
         }
+        return result;
     }
 
     public boolean contains(String key) {
@@ -128,11 +127,15 @@ public class DynamicTask {
     public void execute(){
         if (futureMap.size() > 0) {
             for (String key : futureMap.keySet()) {
-                if (futureMap.get(key).isDone()) {
+                if (futureMap.get(key).isDone() || futureMap.get(key).isCancelled()) {
                     futureMap.remove(key);
                     runnableMap.remove(key);
                 }
             }
         }
     }
+
+    public boolean isAlive(String key) {
+        return futureMap.get(key) != null && !futureMap.get(key).isDone() && !futureMap.get(key).isCancelled();
+    }
 }

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

@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.conf;
 
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.support.spring.http.converter.FastJsonHttpMessageConverter;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
 import org.jetbrains.annotations.NotNull;

+ 3 - 14
src/main/java/com/genersoft/iot/vmp/conf/MediaConfig.java

@@ -2,13 +2,11 @@ package com.genersoft.iot.vmp.conf;
 
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.utils.DateUtil;
-import com.genersoft.iot.vmp.vmanager.gb28181.device.DeviceQuery;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
@@ -27,7 +25,7 @@ public class MediaConfig{
     @Value("${media.ip}")
     private String ip;
 
-    @Value("${media.hook-ip:${sip.ip}}")
+    @Value("${media.hook-ip:}")
     private String hookIp;
 
     @Value("${sip.ip}")
@@ -75,10 +73,6 @@ public class MediaConfig{
     @Value("${media.rtp.port-range}")
     private String rtpPortRange;
 
-
-    @Value("${media.rtp.send-port-range}")
-    private String sendRtpPortRange;
-
     @Value("${media.record-assist-port:0}")
     private Integer recordAssistPort = 0;
 
@@ -92,7 +86,7 @@ public class MediaConfig{
 
     public String getHookIp() {
         if (ObjectUtils.isEmpty(hookIp)){
-            return sipIp;
+            return sipIp.split(",")[0];
         }else {
             return hookIp;
         }
@@ -191,10 +185,6 @@ public class MediaConfig{
         return sipDomain;
     }
 
-    public String getSendRtpPortRange() {
-        return sendRtpPortRange;
-    }
-
     public MediaServerItem getMediaSerItem(){
         MediaServerItem mediaServerItem = new MediaServerItem();
         mediaServerItem.setId(id);
@@ -214,9 +204,8 @@ public class MediaConfig{
         mediaServerItem.setSecret(secret);
         mediaServerItem.setRtpEnable(rtpEnable);
         mediaServerItem.setRtpPortRange(rtpPortRange);
-        mediaServerItem.setSendRtpPortRange(sendRtpPortRange);
         mediaServerItem.setRecordAssistPort(recordAssistPort);
-        mediaServerItem.setHookAliveInterval(120);
+        mediaServerItem.setHookAliveInterval(30.00f);
 
         mediaServerItem.setCreateTime(DateUtil.getNow());
         mediaServerItem.setUpdateTime(DateUtil.getNow());

+ 0 - 1
src/main/java/com/genersoft/iot/vmp/conf/MediaStatusTimerTask.java

@@ -1,6 +1,5 @@
 package com.genersoft.iot.vmp.conf;
 
-import com.alibaba.fastjson.JSONObject;
 import org.springframework.scheduling.annotation.Scheduled;
 
 /**

+ 7 - 22
src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java

@@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.conf;
 
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
 
 @Component
 @ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true)
@@ -10,11 +11,6 @@ public class SipConfig {
 
 	private String ip;
 
-	/**
-	 * 默认使用 0.0.0.0
-	 */
-	private String monitorIp = "0.0.0.0";
-
 	private Integer port;
 
 	private String domain;
@@ -25,8 +21,6 @@ public class SipConfig {
 	
 	Integer ptzSpeed = 50;
 
-	Integer keepaliveTimeOut = 255;
-
 	Integer registerTimeInterval = 120;
 
 	private boolean alarm;
@@ -35,10 +29,6 @@ public class SipConfig {
 		this.ip = ip;
 	}
 
-	public void setMonitorIp(String monitorIp) {
-		this.monitorIp = monitorIp;
-	}
-
 	public void setPort(Integer port) {
 		this.port = port;
 	}
@@ -59,18 +49,11 @@ public class SipConfig {
 		this.ptzSpeed = ptzSpeed;
 	}
 
-	public void setKeepaliveTimeOut(Integer keepaliveTimeOut) {
-		this.keepaliveTimeOut = keepaliveTimeOut;
-	}
 
 	public void setRegisterTimeInterval(Integer registerTimeInterval) {
 		this.registerTimeInterval = registerTimeInterval;
 	}
 
-	public String getMonitorIp() {
-		return monitorIp;
-	}
-
 	public String getIp() {
 		return ip;
 	}
@@ -99,10 +82,6 @@ public class SipConfig {
 		return ptzSpeed;
 	}
 
-	public Integer getKeepaliveTimeOut() {
-		return keepaliveTimeOut;
-	}
-
 	public Integer getRegisterTimeInterval() {
 		return registerTimeInterval;
 	}
@@ -114,4 +93,10 @@ public class SipConfig {
 	public void setAlarm(boolean alarm) {
 		this.alarm = alarm;
 	}
+
+	public void getLocalIp(String deviceLocalIp) {
+		if (ObjectUtils.isEmpty(deviceLocalIp)) {
+
+		}
+	}
 }

+ 1 - 2
src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java

@@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.conf;
 
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
-import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.service.IPlatformService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
@@ -47,7 +46,7 @@ public class SipPlatformRunner implements CommandLineRunner {
             parentPlatformCatch.setId(parentPlatform.getServerGBId());
             redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
             // 设置所有平台离线
-            platformService.offline(parentPlatform);
+            platformService.offline(parentPlatform, true);
             // 取消订阅
             sipCommanderForPlatform.unregister(parentPlatform, null, (eventResult)->{
                 platformService.login(parentPlatform);

+ 5 - 2
src/main/java/com/genersoft/iot/vmp/conf/SystemInfoTimerTask.java

@@ -9,6 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -22,15 +23,17 @@ public class SystemInfoTimerTask {
     @Autowired
     private IRedisCatchStorage redisCatchStorage;
 
-    @Scheduled(fixedRate = 1000)   //每1秒执行一次
+    @Scheduled(fixedRate = 2000)   //每1秒执行一次
     public void execute(){
         try {
             double cpuInfo = SystemInfoUtils.getCpuInfo();
             redisCatchStorage.addCpuInfo(cpuInfo);
             double memInfo = SystemInfoUtils.getMemInfo();
             redisCatchStorage.addMemInfo(memInfo);
-            Map<String, String> networkInterfaces = SystemInfoUtils.getNetworkInterfaces();
+            Map<String, Double> networkInterfaces = SystemInfoUtils.getNetworkInterfaces();
             redisCatchStorage.addNetInfo(networkInterfaces);
+            List<Map<String, Object>> diskInfo =SystemInfoUtils.getDiskInfo();
+            redisCatchStorage.addDiskInfo(diskInfo);
         } catch (InterruptedException e) {
             logger.error("[获取系统信息失败] {}", e.getMessage());
         }

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

@@ -33,8 +33,14 @@ public class UserSetting {
 
     private Boolean usePushingAsStatus = Boolean.TRUE;
 
+    private Boolean useSourceIpAsStreamIp = Boolean.FALSE;
+
     private Boolean streamOnDemand = Boolean.TRUE;
 
+    private Boolean pushAuthority = Boolean.TRUE;
+
+    private Boolean gbSendStreamStrict = Boolean.FALSE;
+
     private String serverId = "000000";
 
     private String thirdPartyGBIdReg = "[\\s\\S]*";
@@ -156,4 +162,28 @@ public class UserSetting {
     public void setStreamOnDemand(Boolean streamOnDemand) {
         this.streamOnDemand = streamOnDemand;
     }
+
+    public Boolean getUseSourceIpAsStreamIp() {
+        return useSourceIpAsStreamIp;
+    }
+
+    public void setUseSourceIpAsStreamIp(Boolean useSourceIpAsStreamIp) {
+        this.useSourceIpAsStreamIp = useSourceIpAsStreamIp;
+    }
+
+    public Boolean getPushAuthority() {
+        return pushAuthority;
+    }
+
+    public void setPushAuthority(Boolean pushAuthority) {
+        this.pushAuthority = pushAuthority;
+    }
+
+    public Boolean getGbSendStreamStrict() {
+        return gbSendStreamStrict;
+    }
+
+    public void setGbSendStreamStrict(Boolean gbSendStreamStrict) {
+        this.gbSendStreamStrict = gbSendStreamStrict;
+    }
 }

+ 1 - 0
src/main/java/com/genersoft/iot/vmp/conf/VersionInfo.java

@@ -19,6 +19,7 @@ public class VersionInfo {
         versionPo.setBUILD_DATE(gitUtil.getBuildDate());
         versionPo.setGIT_Revision_SHORT(gitUtil.getCommitIdShort());
         versionPo.setVersion(gitUtil.getBuildVersion());
+        versionPo.setGIT_DATE(gitUtil.getCommitTime());
 
         return versionPo;
     }

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

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.conf;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import org.springframework.beans.factory.annotation.Autowired;

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

@@ -1,7 +1,6 @@
 package com.genersoft.iot.vmp.conf.redis;
 
 
-import com.alibaba.fastjson.parser.ParserConfig;
 import com.genersoft.iot.vmp.common.VideoManagerConstants;
 import com.genersoft.iot.vmp.service.redisMsg.*;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -55,8 +54,7 @@ public class RedisConfig extends CachingConfigurerSupport {
 		// value值的序列化采用fastJsonRedisSerializer
 		redisTemplate.setValueSerializer(fastJsonRedisSerializer);
 		redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
-		// 全局开启AutoType,不建议使用
-		 ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
+
 		// key的序列化采用StringRedisSerializer
 		redisTemplate.setKeySerializer(new StringRedisSerializer());
 		redisTemplate.setHashKeySerializer(new StringRedisSerializer());

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

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.conf.security;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
 import org.apache.poi.hssf.eventmodel.ERFListener;
 import org.slf4j.Logger;

+ 105 - 56
src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java

@@ -8,16 +8,18 @@ import gov.nist.javax.sip.SipStackImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.DependsOn;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
 
 import javax.sip.*;
-import java.util.Properties;
-import java.util.TooManyListenersException;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 
-@Configuration
-public class SipLayer{
+@Component
+@Order(value=1)
+public class SipLayer implements CommandLineRunner {
 
 	private final static Logger logger = LoggerFactory.getLogger(SipLayer.class);
 
@@ -27,70 +29,117 @@ public class SipLayer{
 	@Autowired
 	private ISIPProcessorObserver sipProcessorObserver;
 
-	private SipStackImpl sipStack;
+
+
+	private final Map<String, SipProviderImpl> tcpSipProviderMap = new ConcurrentHashMap<>();
+	private final Map<String, SipProviderImpl> udpSipProviderMap = new ConcurrentHashMap<>();
 
 	private SipFactory sipFactory;
 
+	@Override
+	public void run(String... args) {
+		List<String> monitorIps = new ArrayList<>();
+		// 使用逗号分割多个ip
+		String separator = ",";
+		if (sipConfig.getIp().indexOf(separator) > 0) {
+			String[] split = sipConfig.getIp().split(separator);
+			monitorIps.addAll(Arrays.asList(split));
+		}else {
+			monitorIps.add(sipConfig.getIp());
+		}
 
-	@Bean("sipFactory")
-	SipFactory createSipFactory() {
 		sipFactory = SipFactory.getInstance();
 		sipFactory.setPathName("gov.nist");
-		return sipFactory;
-	}
-	
-	@Bean("sipStack")
-	@DependsOn({"sipFactory"})
-	SipStackImpl createSipStack() throws PeerUnavailableException {
-		sipStack = ( SipStackImpl )sipFactory.createSipStack(DefaultProperties.getProperties(sipConfig.getMonitorIp(), false));
-		return sipStack;
+		if (monitorIps.size() > 0) {
+			for (String monitorIp : monitorIps) {
+				addListeningPoint(monitorIp, sipConfig.getPort());
+			}
+			if (udpSipProviderMap.size() + tcpSipProviderMap.size() == 0) {
+				System.exit(1);
+			}
+		}
 	}
 
-	@Bean(name = "tcpSipProvider")
-	@DependsOn("sipStack")
-	SipProviderImpl startTcpListener() {
-		ListeningPoint tcpListeningPoint = null;
-		SipProviderImpl tcpSipProvider  = null;
+	private void addListeningPoint(String monitorIp, int port){
+		SipStackImpl sipStack;
+		try {
+			sipStack = (SipStackImpl)sipFactory.createSipStack(DefaultProperties.getProperties(monitorIp, false));
+		} catch (PeerUnavailableException e) {
+			logger.error("[Sip Server] SIP服务启动失败, 监听地址{}失败,请检查ip是否正确", monitorIp);
+			return;
+		}
+
 		try {
-			tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "TCP");
-			tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint);
+			ListeningPoint tcpListeningPoint = sipStack.createListeningPoint(monitorIp, port, "TCP");
+			SipProviderImpl tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint);
+
 			tcpSipProvider.setDialogErrorsAutomaticallyHandled();
 			tcpSipProvider.addSipListener(sipProcessorObserver);
-			logger.info("[Sip Server] TCP 启动成功 {}:{}", sipConfig.getMonitorIp(), sipConfig.getPort());
-		} catch (TransportNotSupportedException e) {
-			e.printStackTrace();
-		} catch (InvalidArgumentException e) {
-			logger.error("[Sip Server]  无法使用 [ {}:{} ]作为SIP[ TCP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
-					, sipConfig.getMonitorIp(), sipConfig.getPort());
-		} catch (TooManyListenersException e) {
-			e.printStackTrace();
-		} catch (ObjectInUseException e) {
-			e.printStackTrace();
+			tcpSipProviderMap.put(monitorIp, tcpSipProvider);
+
+			logger.info("[Sip Server] tcp://{}:{} 启动成功", monitorIp, port);
+		} catch (TransportNotSupportedException
+				 | TooManyListenersException
+				 | ObjectInUseException
+				 | InvalidArgumentException e) {
+			logger.error("[Sip Server] tcp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确"
+					, monitorIp, port);
 		}
-		return tcpSipProvider;
-	}
-	
-	@Bean(name = "udpSipProvider")
-	@DependsOn("sipStack")
-	SipProviderImpl startUdpListener() {
-		ListeningPoint udpListeningPoint = null;
-		SipProviderImpl udpSipProvider = null;
+
 		try {
-			udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "UDP");
-			udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
+			ListeningPoint udpListeningPoint = sipStack.createListeningPoint(monitorIp, port, "UDP");
+
+			SipProviderImpl udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
 			udpSipProvider.addSipListener(sipProcessorObserver);
-		} catch (TransportNotSupportedException e) {
-			e.printStackTrace();
-		} catch (InvalidArgumentException e) {
-			logger.error("[Sip Server]  无法使用 [ {}:{} ]作为SIP[ UDP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
-					, sipConfig.getMonitorIp(), sipConfig.getPort());
-		} catch (TooManyListenersException e) {
-			e.printStackTrace();
-		} catch (ObjectInUseException e) {
-			e.printStackTrace();
+
+			udpSipProviderMap.put(monitorIp, udpSipProvider);
+
+			logger.info("[Sip Server] udp://{}:{} 启动成功", monitorIp, port);
+		} catch (TransportNotSupportedException
+				 | TooManyListenersException
+				 | ObjectInUseException
+				 | InvalidArgumentException e) {
+			logger.error("[Sip Server] udp://{}:{} SIP服务启动失败,请检查端口是否被占用或者ip是否正确"
+					, monitorIp, port);
 		}
-		logger.info("[Sip Server] UDP 启动成功 {}:{}", sipConfig.getMonitorIp(), sipConfig.getPort());
-		return udpSipProvider;
 	}
 
+	public SipFactory getSipFactory() {
+		return sipFactory;
+	}
+
+	public SipProviderImpl getUdpSipProvider(String ip) {
+		if (ObjectUtils.isEmpty(ip)) {
+			return null;
+		}
+		return udpSipProviderMap.get(ip);
+	}
+
+	public SipProviderImpl getUdpSipProvider() {
+		if (udpSipProviderMap.size() != 1) {
+			return null;
+		}
+		return udpSipProviderMap.values().stream().findFirst().get();
+	}
+
+	public SipProviderImpl getTcpSipProvider() {
+		if (tcpSipProviderMap.size() != 1) {
+			return null;
+		}
+		return tcpSipProviderMap.values().stream().findFirst().get();
+	}
+
+	public SipProviderImpl getTcpSipProvider(String ip) {
+		if (ObjectUtils.isEmpty(ip)) {
+			return null;
+		}
+		return tcpSipProviderMap.get(ip);
+	}
+
+	public String getLocalIp(String deviceLocalIp) {
+		if (!ObjectUtils.isEmpty(deviceLocalIp)) {
+			return deviceLocalIp;
+		}
+		return getUdpSipProvider().getListeningPoint().getIPAddress();
+	}
 }

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

@@ -172,6 +172,15 @@ public class Device {
 	@Schema(description = "树类型 国标规定了两种树的展现方式 行政区划:CivilCode 和业务分组:BusinessGroup")
 	private String treeType;
 
+	@Schema(description = "密码")
+	private String password;
+
+	@Schema(description = "收流IP")
+	private String sdpIp;
+
+	@Schema(description = "SIP交互IP(设备访问平台的IP)")
+	private String localIp;
+
 
 	public String getDeviceId() {
 		return deviceId;
@@ -382,4 +391,27 @@ public class Device {
 		this.treeType = treeType;
 	}
 
+	public String getPassword() {
+		return password;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	public String getSdpIp() {
+		return sdpIp;
+	}
+
+	public void setSdpIp(String sdpIp) {
+		this.sdpIp = sdpIp;
+	}
+
+	public String getLocalIp() {
+		return localIp;
+	}
+
+	public void setLocalIp(String localIp) {
+		this.localIp = localIp;
+	}
 }

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/bean/InviteStreamInfo.java

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.bean;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 
 public class InviteStreamInfo {

+ 1 - 3
src/main/java/com/genersoft/iot/vmp/gb28181/bean/SubscribeInfo.java

@@ -3,15 +3,13 @@ package com.genersoft.iot.vmp.gb28181.bean;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPResponse;
 
-import javax.sip.ServerTransaction;
 import javax.sip.header.*;
 
 public class SubscribeInfo {
 
 
-    public SubscribeInfo(ServerTransaction serverTransaction, String id) {
+    public SubscribeInfo(SIPRequest request, String id) {
         this.id = id;
-        SIPRequest request = (SIPRequest)serverTransaction.getRequest();
         this.request = request;
         this.expires = request.getExpires().getExpires();
         EventHeader eventHeader = (EventHeader)request.getHeader(EventHeader.NAME);

+ 1 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/conf/SipLoggerPass.java

@@ -6,6 +6,7 @@ import java.util.Properties;
 
 /**
  * sip日志格式化
+ * 暂不使用
  */
 public class SipLoggerPass implements StackLogger {
 

+ 5 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java

@@ -8,10 +8,12 @@ import org.slf4j.LoggerFactory;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
-import javax.sip.*;
+import javax.sip.DialogTerminatedEvent;
+import javax.sip.ResponseEvent;
+import javax.sip.TimeoutEvent;
+import javax.sip.TransactionTerminatedEvent;
 import javax.sip.header.CallIdHeader;
 import javax.sip.message.Response;
-import java.text.ParseException;
 import java.time.Instant;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -30,6 +32,7 @@ public class SipSubscribe {
     private Map<String, SipSubscribe.Event> okSubscribes = new ConcurrentHashMap<>();
 
     private Map<String, Instant> okTimeSubscribes = new ConcurrentHashMap<>();
+
     private Map<String, Instant> errorTimeSubscribes = new ConcurrentHashMap<>();
 
     //    @Scheduled(cron="*/5 * * * * ?")   //每五秒执行一次

+ 5 - 9
src/main/java/com/genersoft/iot/vmp/gb28181/event/record/RecordEndEventListener.java

@@ -1,18 +1,16 @@
 package com.genersoft.iot.vmp.gb28181.event.record;
 
 import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
-import com.genersoft.iot.vmp.gb28181.bean.RecordItem;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.context.ApplicationListener;
 import org.springframework.stereotype.Component;
-import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
 
-import java.io.IOException;
-import java.util.*;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
- * @description: 录像查询结束时间
+ * @description: 录像查询结束事件
  * @author: pan
  * @data: 2022-02-23
  */
@@ -22,13 +20,12 @@ public class RecordEndEventListener implements ApplicationListener<RecordEndEven
 
     private final static Logger logger = LoggerFactory.getLogger(RecordEndEventListener.class);
 
-    private static Map<String, SseEmitter> sseEmitters = new Hashtable<>();
-
     public interface RecordEndEventHandler{
         void  handler(RecordInfo recordInfo);
     }
 
-    private Map<String, RecordEndEventHandler> handlerMap = new HashMap<>();
+    private Map<String, RecordEndEventHandler> handlerMap = new ConcurrentHashMap<>();
+
     @Override
     public void onApplicationEvent(RecordEndEvent event) {
         logger.info("录像查询完成事件触发,deviceId:{}, channelId: {}, 录像数量{}条", event.getRecordInfo().getDeviceId(),
@@ -38,7 +35,6 @@ public class RecordEndEventListener implements ApplicationListener<RecordEndEven
                 recordEndEventHandler.handler(event.getRecordInfo());
             }
         }
-
     }
 
     public void addEndEventHandler(String device, String channelId, RecordEndEventHandler recordEndEventHandler) {

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java

@@ -42,7 +42,7 @@ public class CatalogDataCatch {
             catalogData.setSn(sn);
             catalogData.setTotal(total);
             catalogData.setDevice(device);
-            catalogData.setChannelList(Collections.synchronizedList(new ArrayList<>()));
+            catalogData.setChannelList(deviceChannelList);
             catalogData.setStatus(CatalogData.CatalogDataStatus.runIng);
             catalogData.setLastTime(Instant.now());
             data.put(deviceId, catalogData);

+ 0 - 51
src/main/java/com/genersoft/iot/vmp/gb28181/task/SipDeviceRunner.java

@@ -1,51 +0,0 @@
-package com.genersoft.iot.vmp.gb28181.task;
-
-import com.genersoft.iot.vmp.conf.UserSetting;
-import com.genersoft.iot.vmp.gb28181.bean.Device;
-import com.genersoft.iot.vmp.service.IDeviceService;
-import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.CommandLineRunner;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Component;
-
-import java.util.ArrayList;
-import java.util.List;
-
-
-/**
- * 系统启动时控制设备
- * @author lin
- */
-@Component
-@Order(value=4)
-public class SipDeviceRunner implements CommandLineRunner {
-
-    @Autowired
-    private IVideoManagerStorage storager;
-
-    @Autowired
-    private IRedisCatchStorage redisCatchStorage;
-
-    @Autowired
-    private UserSetting userSetting;
-
-    @Autowired
-    private IDeviceService deviceService;
-
-    @Override
-    public void run(String... args) throws Exception {
-        List<Device> deviceList = deviceService.getAllOnlineDevice();
-
-        for (Device device : deviceList) {
-            if (deviceService.expire(device)){
-                deviceService.offline(device.getDeviceId());
-            }else {
-                deviceService.online(device);
-            }
-        }
-        // 重置cseq计数
-        redisCatchStorage.resetAllCSEQ();
-    }
-}

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

@@ -0,0 +1,95 @@
+package com.genersoft.iot.vmp.gb28181.task;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.genersoft.iot.vmp.conf.UserSetting;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
+import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
+import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
+import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.service.IDeviceService;
+import com.genersoft.iot.vmp.service.IMediaServerService;
+import com.genersoft.iot.vmp.service.IPlatformService;
+import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 系统启动时控制设备
+ * @author lin
+ */
+@Component
+@Order(value=4)
+public class SipRunner implements CommandLineRunner {
+
+    @Autowired
+    private IVideoManagerStorage storager;
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private UserSetting userSetting;
+
+    @Autowired
+    private IDeviceService deviceService;
+
+    @Autowired
+    private ZLMRESTfulUtils zlmresTfulUtils;
+
+    @Autowired
+    private IMediaServerService mediaServerService;
+
+    @Autowired
+    private IPlatformService platformService;
+
+    @Autowired
+    private ISIPCommanderForPlatform commanderForPlatform;
+
+    @Override
+    public void run(String... args) throws Exception {
+        List<Device> deviceList = deviceService.getAllOnlineDevice();
+
+        for (Device device : deviceList) {
+            if (deviceService.expire(device)){
+                deviceService.offline(device.getDeviceId());
+            }else {
+                deviceService.online(device);
+            }
+        }
+        // 重置cseq计数
+        redisCatchStorage.resetAllCSEQ();
+        // 清理redis
+        // 查找国标推流
+        List<SendRtpItem> sendRtpItems = redisCatchStorage.queryAllSendRTPServer();
+        if (sendRtpItems.size() > 0) {
+            for (SendRtpItem sendRtpItem : sendRtpItems) {
+                MediaServerItem mediaServerItem = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+                redisCatchStorage.deleteSendRTPServer(sendRtpItem.getPlatformId(),sendRtpItem.getChannelId(), sendRtpItem.getCallId(),sendRtpItem.getStreamId());
+                if (mediaServerItem != null) {
+                    Map<String, Object> param = new HashMap<>();
+                    param.put("vhost","__defaultVhost__");
+                    param.put("app",sendRtpItem.getApp());
+                    param.put("stream",sendRtpItem.getStreamId());
+                    param.put("ssrc",sendRtpItem.getSsrc());
+                    JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param);
+                    if (jsonObject != null && jsonObject.getInteger("code") == 0) {
+                        ParentPlatform platform = platformService.queryPlatformByServerGBId(sendRtpItem.getPlatformId());
+                        if (platform != null) {
+                            commanderForPlatform.streamByeCmd(platform, sendRtpItem.getCallId());
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 3 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java

@@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.event.response.ISIPResponseProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.event.timeout.ITimeoutProcessor;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -15,6 +16,7 @@ import javax.sip.*;
 import javax.sip.header.*;
 import javax.sip.message.Request;
 import javax.sip.message.Response;
+import java.net.InetAddress;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -75,6 +77,7 @@ public class SIPProcessorObserver implements ISIPProcessorObserver {
         ISIPRequestProcessor sipRequestProcessor = requestProcessorMap.get(method);
         if (sipRequestProcessor == null) {
             logger.warn("不支持方法{}的request", method);
+            // TODO 回复错误玛
             return;
         }
         requestProcessorMap.get(method).process(requestEvent);

+ 132 - 0
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java

@@ -0,0 +1,132 @@
+package com.genersoft.iot.vmp.gb28181.transmit;
+
+import com.genersoft.iot.vmp.gb28181.SipLayer;
+import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
+import com.genersoft.iot.vmp.utils.GitUtil;
+import gov.nist.javax.sip.SipProviderImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
+
+import javax.sip.SipException;
+import javax.sip.header.CallIdHeader;
+import javax.sip.header.UserAgentHeader;
+import javax.sip.header.ViaHeader;
+import javax.sip.message.Message;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+import java.text.ParseException;
+
+/**
+ * 发送SIP消息
+ * @author lin
+ */
+@Component
+public class SIPSender {
+
+    private final Logger logger = LoggerFactory.getLogger(SIPSender.class);
+
+    @Autowired
+    private SipLayer sipLayer;
+
+    @Autowired
+    private GitUtil gitUtil;
+
+    @Autowired
+    private SipSubscribe sipSubscribe;
+
+    public void transmitRequest(String ip, Message message) throws SipException, ParseException {
+        transmitRequest(ip, message, null, null);
+    }
+
+    public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent) throws SipException, ParseException {
+        transmitRequest(ip, message, errorEvent, null);
+    }
+
+    public void transmitRequest(String ip, Message message, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, ParseException {
+        ViaHeader viaHeader = (ViaHeader)message.getHeader(ViaHeader.NAME);
+        String transport = "UDP";
+        if (viaHeader == null) {
+            logger.warn("[消息头缺失]: ViaHeader, 使用默认的UDP方式处理数据");
+        }else {
+            transport = viaHeader.getTransport();
+        }
+        if (message.getHeader(UserAgentHeader.NAME) == null) {
+            try {
+                message.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
+            } catch (ParseException e) {
+                logger.error("添加UserAgentHeader失败", e);
+            }
+        }
+
+        CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME);
+        // 添加错误订阅
+        if (errorEvent != null) {
+            sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
+                errorEvent.response(eventResult);
+                sipSubscribe.removeErrorSubscribe(eventResult.callId);
+                sipSubscribe.removeOkSubscribe(eventResult.callId);
+            }));
+        }
+        // 添加订阅
+        if (okEvent != null) {
+            sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
+                okEvent.response(eventResult);
+                sipSubscribe.removeOkSubscribe(eventResult.callId);
+                sipSubscribe.removeErrorSubscribe(eventResult.callId);
+            });
+        }
+        if ("TCP".equals(transport)) {
+            SipProviderImpl tcpSipProvider = sipLayer.getTcpSipProvider(ip);
+            if (tcpSipProvider == null) {
+                logger.error("[发送信息失败] 未找到tcp://{}的监听信息", ip);
+                return;
+            }
+            if (message instanceof Request) {
+                tcpSipProvider.sendRequest((Request)message);
+            }else if (message instanceof Response) {
+                tcpSipProvider.sendResponse((Response)message);
+            }
+
+        } else if ("UDP".equals(transport)) {
+            SipProviderImpl sipProvider = sipLayer.getUdpSipProvider(ip);
+            if (sipProvider == null) {
+                logger.error("[发送信息失败] 未找到udp://{}的监听信息", ip);
+                return;
+            }
+            if (message instanceof Request) {
+                sipProvider.sendRequest((Request)message);
+            }else if (message instanceof Response) {
+                sipProvider.sendResponse((Response)message);
+            }
+        }
+    }
+
+    public CallIdHeader getNewCallIdHeader(String ip, String transport){
+        if (ObjectUtils.isEmpty(transport)) {
+            return sipLayer.getUdpSipProvider().getNewCallId();
+        }
+        SipProviderImpl sipProvider;
+        if (ObjectUtils.isEmpty(ip)) {
+            sipProvider = transport.equalsIgnoreCase("TCP") ? sipLayer.getTcpSipProvider()
+                    : sipLayer.getUdpSipProvider();
+        }else {
+            sipProvider = transport.equalsIgnoreCase("TCP") ? sipLayer.getTcpSipProvider(ip)
+                    : sipLayer.getUdpSipProvider(ip);
+        }
+
+        if (sipProvider == null) {
+            sipProvider = sipLayer.getUdpSipProvider();
+        }
+
+        if (sipProvider != null) {
+            return sipProvider.getNewCallId();
+        }else {
+            logger.warn("[新建CallIdHeader失败], ip={}, transport={}", ip, transport);
+            return null;
+        }
+    }
+}

+ 52 - 23
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java

@@ -1,15 +1,15 @@
 package com.genersoft.iot.vmp.gb28181.transmit.callback;
 
-import java.util.HashMap;
+import com.genersoft.iot.vmp.vmanager.bean.DeferredResultEx;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
+import org.springframework.web.context.request.async.DeferredResult;
+
+import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Component;
-import org.springframework.web.context.request.async.DeferredResult;
-
 /**    
  * @description: 异步请求处理
  * @author: swwheihei
@@ -51,31 +51,48 @@ public class DeferredResultHolder {
 
 	public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST";
 
-	private Map<String, Map<String, DeferredResult>> map = new ConcurrentHashMap<>();
+	private Map<String, Map<String, DeferredResultEx>> map = new ConcurrentHashMap<>();
 
 
-	public void put(String key, String id, DeferredResult result) {
-		Map<String, DeferredResult> deferredResultMap = map.get(key);
+	public void put(String key, String id, DeferredResultEx result) {
+		Map<String, DeferredResultEx> deferredResultMap = map.get(key);
 		if (deferredResultMap == null) {
 			deferredResultMap = new ConcurrentHashMap<>();
 			map.put(key, deferredResultMap);
 		}
 		deferredResultMap.put(id, result);
 	}
-	
-	public DeferredResult get(String key, String id) {
-		Map<String, DeferredResult> deferredResultMap = map.get(key);
+
+	public void put(String key, String id, DeferredResult result) {
+		Map<String, DeferredResultEx> deferredResultMap = map.get(key);
 		if (deferredResultMap == null) {
+			deferredResultMap = new ConcurrentHashMap<>();
+			map.put(key, deferredResultMap);
+		}
+		deferredResultMap.put(id, new DeferredResultEx(result));
+	}
+	
+	public DeferredResultEx get(String key, String id) {
+		Map<String, DeferredResultEx> deferredResultMap = map.get(key);
+		if (deferredResultMap == null || ObjectUtils.isEmpty(id)) {
 			return null;
 		}
 		return deferredResultMap.get(id);
 	}
 
+	public Collection<DeferredResultEx> getAllByKey(String key) {
+		Map<String, DeferredResultEx> deferredResultMap = map.get(key);
+		if (deferredResultMap == null) {
+			return null;
+		}
+		return deferredResultMap.values();
+	}
+
 	public boolean exist(String key, String id){
 		if (key == null) {
 			return false;
 		}
-		Map<String, DeferredResult> deferredResultMap = map.get(key);
+		Map<String, DeferredResultEx> deferredResultMap = map.get(key);
 		if (id == null) {
 			return deferredResultMap != null;
 		}else {
@@ -88,15 +105,15 @@ public class DeferredResultHolder {
 	 * @param msg
 	 */
 	public void invokeResult(RequestMessage msg) {
-		Map<String, DeferredResult> deferredResultMap = map.get(msg.getKey());
+		Map<String, DeferredResultEx> deferredResultMap = map.get(msg.getKey());
 		if (deferredResultMap == null) {
 			return;
 		}
-		DeferredResult result = deferredResultMap.get(msg.getId());
+		DeferredResultEx result = deferredResultMap.get(msg.getId());
 		if (result == null) {
 			return;
 		}
-		result.setResult(msg.getData());
+		result.getDeferredResult().setResult(msg.getData());
 		deferredResultMap.remove(msg.getId());
 		if (deferredResultMap.size() == 0) {
 			map.remove(msg.getKey());
@@ -108,18 +125,30 @@ public class DeferredResultHolder {
 	 * @param msg
 	 */
 	public void invokeAllResult(RequestMessage msg) {
-		Map<String, DeferredResult> deferredResultMap = map.get(msg.getKey());
+		Map<String, DeferredResultEx> deferredResultMap = map.get(msg.getKey());
 		if (deferredResultMap == null) {
 			return;
 		}
-		Set<String> ids = deferredResultMap.keySet();
-		for (String id : ids) {
-			DeferredResult result = deferredResultMap.get(id);
-			if (result == null) {
+		synchronized (this) {
+			deferredResultMap = map.get(msg.getKey());
+			if (deferredResultMap == null) {
 				return;
 			}
-			result.setResult(msg.getData());
+			Set<String> ids = deferredResultMap.keySet();
+			for (String id : ids) {
+				DeferredResultEx result = deferredResultMap.get(id);
+				if (result == null) {
+					return;
+				}
+				if (result.getFilter() != null) {
+					Object handler = result.getFilter().handler(msg.getData());
+					result.getDeferredResult().setResult(handler);
+				}else {
+					result.getDeferredResult().setResult(msg.getData());
+				}
+
+			}
+			map.remove(msg.getKey());
 		}
-		map.remove(msg.getKey());
 	}
 }

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

@@ -12,6 +12,8 @@ import gov.nist.javax.sip.message.SIPRequest;
 import javax.sip.InvalidArgumentException;
 import javax.sip.PeerUnavailableException;
 import javax.sip.SipException;
+import javax.sip.message.Message;
+import javax.sip.message.Request;
 import java.text.ParseException;
 import javax.sip.message.Request;
 
@@ -359,9 +361,4 @@ public interface ISIPCommander {
 	 */
 	void sendAlarmMessage(Device device, DeviceAlarm deviceAlarm) throws InvalidArgumentException, SipException, ParseException;
 
-	void transmitRequest(String transport, Request request) throws SipException, ParseException ;
-
-	void transmitRequest(String transport, Request request, SipSubscribe.Event errorEvent) throws SipException, ParseException;
-
-	void transmitRequest(String transport, Request request, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException, ParseException;
 }

+ 74 - 73
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java

@@ -1,6 +1,7 @@
 package com.genersoft.iot.vmp.gb28181.transmit.cmd;
 
 import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.SipLayer;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
 import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
@@ -36,7 +37,7 @@ public class SIPRequestHeaderPlarformProvider {
 	private SipConfig sipConfig;
 	
 	@Autowired
-	private SipFactory sipFactory;
+	private SipLayer sipLayer;
 
 	@Autowired
 	private GitUtil gitUtil;
@@ -44,42 +45,42 @@ public class SIPRequestHeaderPlarformProvider {
 	@Autowired
 	private IRedisCatchStorage redisCatchStorage;
 
-	public Request createRegisterRequest(@NotNull ParentPlatform platform, 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 viaTag, CallIdHeader callIdHeader, boolean isRegister) throws ParseException, InvalidArgumentException, PeerUnavailableException {
 		Request request = null;
-		String sipAddress = sipConfig.getIp() + ":" + sipConfig.getPort();
+		String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort();
 		//请求行
-		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(platform.getServerGBId(),
-				platform.getServerIP() + ":" + platform.getServerPort());
+		SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(),
+				parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
 		//via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
-		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(platform.getServerIP(), platform.getServerPort(), platform.getTransport(), viaTag);
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(), parentPlatform.getServerPort(), parentPlatform.getTransport(), viaTag);
 		viaHeader.setRPort();
 		viaHeaders.add(viaHeader);
 		//from
-		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(platform.getDeviceGBId(), sipConfig.getDomain());
-		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
-		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
+		SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
+		Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag);
 		//to
-		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(platform.getDeviceGBId(), sipConfig.getDomain());
-		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
-		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
+		SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
+		Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null);
 
 		//Forwards
-		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
 
 		//ceq
-		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(CSeq, Request.REGISTER);
-		request = sipFactory.createMessageFactory().createRequest(requestLine, Request.REGISTER, callIdHeader,
+		CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(CSeq, Request.REGISTER);
+		request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.REGISTER, callIdHeader,
 				cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
 
-		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory()
-				.createSipURI(platform.getDeviceGBId(), sipAddress));
-		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+		Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory()
+				.createSipURI(parentPlatform.getDeviceGBId(), sipAddress));
+		request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
 
-		ExpiresHeader expires = sipFactory.createHeaderFactory().createExpiresHeader(isRegister ? platform.getExpires() : 0);
+		ExpiresHeader expires = sipLayer.getSipFactory().createHeaderFactory().createExpiresHeader(isRegister ? parentPlatform.getExpires() : 0);
 		request.addHeader(expires);
 
-		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
 
 		return request;
 	}
@@ -89,9 +90,9 @@ public class SIPRequestHeaderPlarformProvider {
 
 
 		Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(), fromTag, viaTag, callIdHeader, isRegister);
-		SipURI requestURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
+		SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
 		if (www == null) {
-			AuthorizationHeader authorizationHeader = sipFactory.createHeaderFactory().createAuthorizationHeader("Digest");
+			AuthorizationHeader authorizationHeader = sipLayer.getSipFactory().createHeaderFactory().createAuthorizationHeader("Digest");
 			authorizationHeader.setUsername(parentPlatform.getDeviceGBId());
 			authorizationHeader.setURI(requestURI);
 			authorizationHeader.setAlgorithm("MD5");
@@ -140,7 +141,7 @@ public class SIPRequestHeaderPlarformProvider {
 
 		String RESPONSE = DigestUtils.md5DigestAsHex(reStr.toString().getBytes());
 
-		AuthorizationHeader authorizationHeader = sipFactory.createHeaderFactory().createAuthorizationHeader(scheme);
+		AuthorizationHeader authorizationHeader = sipLayer.getSipFactory().createHeaderFactory().createAuthorizationHeader(scheme);
 		authorizationHeader.setUsername(parentPlatform.getDeviceGBId());
 		authorizationHeader.setRealm(realm);
 		authorizationHeader.setNonce(nonce);
@@ -158,7 +159,7 @@ public class SIPRequestHeaderPlarformProvider {
 	}
 
 	public Request createMessageRequest(ParentPlatform parentPlatform, String content, SendRtpItem sendRtpItem) throws PeerUnavailableException, ParseException, InvalidArgumentException {
-		CallIdHeader callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId());
+		CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId());
 		return createMessageRequest(parentPlatform, content, sendRtpItem.getToTag(), SipUtils.getNewViaTag(), sendRtpItem.getFromTag(), callIdHeader);
 	}
 
@@ -171,36 +172,36 @@ public class SIPRequestHeaderPlarformProvider {
 		Request request = null;
 		String serverAddress = parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort();
 		// sipuri
-		SipURI requestURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress);
+		SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress);
 		// via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
-		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()),
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()),
 				parentPlatform.getTransport(), viaTag);
 		viaHeader.setRPort();
 		viaHeaders.add(viaHeader);
 		// from
-		// SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), parentPlatform.getDeviceIp() + ":" + parentPlatform.getDeviceIp());
-		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
-		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
-		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
+		// SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), parentPlatform.getDeviceIp() + ":" + parentPlatform.getDeviceIp());
+		SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
+		Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag);
 		// to
-		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress);
-		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
-		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);
+		SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), serverAddress);
+		Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, toTag);
 
 		// Forwards
-		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
 		// ceq
-		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
-		MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipFactory.createMessageFactory();
+		CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
+		MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipLayer.getSipFactory().createMessageFactory();
 		// 设置编码, 防止中文乱码
 		messageFactory.setDefaultContentEncodingCharset(parentPlatform.getCharacterSet());
 		request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
 				toHeader, viaHeaders, maxForwards);
 
-		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
 
-		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
+		ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
 		request.setContent(content, contentTypeHeader);
 		return request;
 	}
@@ -208,54 +209,54 @@ public class SIPRequestHeaderPlarformProvider {
 	public SIPRequest createNotifyRequest(ParentPlatform parentPlatform, String content, SubscribeInfo subscribeInfo) throws PeerUnavailableException, ParseException, InvalidArgumentException {
 		SIPRequest request = null;
 		// sipuri
-		SipURI requestURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort());
+		SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort());
 		// via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
-		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()),
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()),
 				parentPlatform.getTransport(), SipUtils.getNewViaTag());
 		viaHeader.setRPort();
 		viaHeaders.add(viaHeader);
 		// from
-		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(),
+		SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(),
 				parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort());
-		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
-		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, subscribeInfo.getResponse().getToTag());
+		Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, subscribeInfo.getResponse().getToTag());
 		// to
-		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerGBDomain());
-		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
-		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, subscribeInfo.getRequest().getFromTag());
+		SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerGBDomain());
+		Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, subscribeInfo.getRequest().getFromTag());
 
 		// Forwards
-		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
 		// ceq
-		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.NOTIFY);
-		MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipFactory.createMessageFactory();
+		CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.NOTIFY);
+		MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipLayer.getSipFactory().createMessageFactory();
 		// 设置编码, 防止中文乱码
 		messageFactory.setDefaultContentEncodingCharset("gb2312");
 
-		CallIdHeader callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(subscribeInfo.getRequest().getCallIdHeader().getCallId());
+		CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(subscribeInfo.getRequest().getCallIdHeader().getCallId());
 
 		request = (SIPRequest) messageFactory.createRequest(requestURI, Request.NOTIFY, callIdHeader, cSeqHeader, fromHeader,
 				toHeader, viaHeaders, maxForwards);
 
-		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
 
-		EventHeader event = sipFactory.createHeaderFactory().createEventHeader(subscribeInfo.getEventType());
+		EventHeader event = sipLayer.getSipFactory().createHeaderFactory().createEventHeader(subscribeInfo.getEventType());
 		if (subscribeInfo.getEventId() != null) {
 			event.setEventId(subscribeInfo.getEventId());
 		}
 
 		request.addHeader(event);
 
-		SubscriptionStateHeader active = sipFactory.createHeaderFactory().createSubscriptionStateHeader("active");
+		SubscriptionStateHeader active = sipLayer.getSipFactory().createHeaderFactory().createSubscriptionStateHeader("active");
 		request.setHeader(active);
 
-		String sipAddress = sipConfig.getIp() + ":" + sipConfig.getPort();
-		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory()
+		String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort();
+		Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory()
 				.createSipURI(parentPlatform.getDeviceGBId(), sipAddress));
-		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+		request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
 
-		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
+		ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
 		request.setContent(content, contentTypeHeader);
 		return request;
     }
@@ -268,42 +269,42 @@ public class SIPRequestHeaderPlarformProvider {
 
 		SIPRequest request = null;
 		// sipuri
-		SipURI requestURI = sipFactory.createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+ ":" + platform.getServerPort());
+		SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerIP()+ ":" + platform.getServerPort());
 		// via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<>();
-		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(platform.getDeviceIp(), Integer.parseInt(platform.getDevicePort()),
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(platform.getDeviceIp(), Integer.parseInt(platform.getDevicePort()),
 				platform.getTransport(), SipUtils.getNewViaTag());
 		viaHeader.setRPort();
 		viaHeaders.add(viaHeader);
 		// from
-		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(platform.getDeviceGBId(),
+		SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(platform.getDeviceGBId(),
 				platform.getDeviceIp() + ":" + platform.getDevicePort());
-		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
-		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, sendRtpItem.getToTag());
+		Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, sendRtpItem.getToTag());
 		// to
-		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerGBDomain());
-		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
-		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, sendRtpItem.getFromTag());
+		SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(platform.getServerGBId(), platform.getServerGBDomain());
+		Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, sendRtpItem.getFromTag());
 
 		// Forwards
-		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
 		// ceq
-		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
-		MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipFactory.createMessageFactory();
+		CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
+		MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipLayer.getSipFactory().createMessageFactory();
 		// 设置编码, 防止中文乱码
 		messageFactory.setDefaultContentEncodingCharset("gb2312");
 
-		CallIdHeader callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId());
+		CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(sendRtpItem.getCallId());
 
 		request = (SIPRequest) messageFactory.createRequest(requestURI, Request.BYE, callIdHeader, cSeqHeader, fromHeader,
 				toHeader, viaHeaders, maxForwards);
 
-		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
 
-		String sipAddress = sipConfig.getIp() + ":" + sipConfig.getPort();
-		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory()
+		String sipAddress = platform.getDeviceIp() + ":" + platform.getDevicePort();
+		Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory()
 				.createSipURI(platform.getDeviceGBId(), sipAddress));
-		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+		request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
 
 		return request;
 	}

+ 121 - 133
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java

@@ -1,33 +1,27 @@
 package com.genersoft.iot.vmp.gb28181.transmit.cmd;
 
-import java.text.ParseException;
-import java.util.ArrayList;
-
-import javax.sip.*;
-import javax.sip.address.Address;
-import javax.sip.address.SipURI;
-import javax.sip.header.*;
-import javax.sip.message.Request;
-
-import com.genersoft.iot.vmp.common.StreamInfo;
-import com.genersoft.iot.vmp.gb28181.bean.SipMsgInfo;
+import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.SipLayer;
+import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
-import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.utils.GitUtil;
-import gov.nist.javax.sip.SipProviderImpl;
-import gov.nist.javax.sip.SipStackImpl;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPResponse;
-import gov.nist.javax.sip.stack.SIPDialog;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Component;
 
-import com.genersoft.iot.vmp.conf.SipConfig;
-import com.genersoft.iot.vmp.gb28181.bean.Device;
+import javax.sip.InvalidArgumentException;
+import javax.sip.PeerUnavailableException;
+import javax.sip.SipException;
+import javax.sip.address.Address;
+import javax.sip.address.SipURI;
+import javax.sip.header.*;
+import javax.sip.message.Request;
+import java.text.ParseException;
+import java.util.ArrayList;
 
 /**
  * @description:摄像头命令request创造器 TODO 冗余代码太多待优化
@@ -41,7 +35,7 @@ public class SIPRequestHeaderProvider {
 	private SipConfig sipConfig;
 	
 	@Autowired
-	private SipFactory sipFactory;
+	private SipLayer sipLayer;
 
 	@Autowired
 	private GitUtil gitUtil;
@@ -51,44 +45,36 @@ public class SIPRequestHeaderProvider {
 
 	@Autowired
 	private VideoStreamSessionManager streamSession;
-
-	@Autowired
-	@Qualifier(value="tcpSipProvider")
-	private SipProviderImpl tcpSipProvider;
-
-	@Autowired
-	@Qualifier(value="udpSipProvider")
-	private SipProviderImpl udpSipProvider;
 	
 	public Request createMessageRequest(Device device, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
 		Request request = null;
 		// sipuri
-		SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
+		SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
 		// via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
-		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
 		viaHeader.setRPort();
 		viaHeaders.add(viaHeader);
 		// from
-		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
-		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
-		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
+		SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
+		Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag);
 		// to
-		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
-		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
-		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);
+		SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
+		Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, toTag);
 
 		// Forwards
-		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
 		// ceq
-		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
+		CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.MESSAGE);
 
-		request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
+		request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
 				toHeader, viaHeaders, maxForwards);
 
-		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
 
-		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
+		ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
 		request.setContent(content, contentTypeHeader);
 		return request;
 	}
@@ -96,38 +82,39 @@ public class SIPRequestHeaderProvider {
 	public Request createInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, String ssrc, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
 		Request request = null;
 		//请求行
-		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
+		SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
 		//via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
-		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
+		HeaderFactory headerFactory = sipLayer.getSipFactory().createHeaderFactory();
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
 		viaHeader.setRPort();
 		viaHeaders.add(viaHeader);
 
 		//from
-		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
-		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
-		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
+		SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
+		Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
 		//to
-		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
-		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
-		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
+		SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
+		Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null);
 		
 		//Forwards
-		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
 		
 		//ceq
-		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE);
-		request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
+		CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE);
+		request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
 
-		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
 
-		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
-		// Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort()));
-		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+		Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
+		// Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort()));
+		request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
 		// Subject
-		SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
+		SubjectHeader subjectHeader = sipLayer.getSipFactory().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
 		request.addHeader(subjectHeader);
-		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
+		ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
 		request.setContent(content, contentTypeHeader);
 		return request;
 	}
@@ -135,39 +122,39 @@ public class SIPRequestHeaderProvider {
 	public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader, String ssrc) throws ParseException, InvalidArgumentException, PeerUnavailableException {
 		Request request = null;
 		//请求行
-		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
+		SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
 		// via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
-		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), viaTag);
 		viaHeader.setRPort();
 		viaHeaders.add(viaHeader);
 		//from
-		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
-		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
-		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
+		SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
+		Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, fromTag); //必须要有标记,否则无法创建会话,无法回应ack
 		//to
-		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
-		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
-		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
+		SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
+		Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null);
 		
 		//Forwards
-		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
 		
 		//ceq
-		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE);
-		request = sipFactory.createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
+		CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INVITE);
+		request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.INVITE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
 		
-		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
-		// Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort()));
-		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+		Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
+		// Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), device.getHost().getIp()+":"+device.getHost().getPort()));
+		request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
 
-		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
 
 		// Subject
-		SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
+		SubjectHeader subjectHeader = sipLayer.getSipFactory().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
 		request.addHeader(subjectHeader);
 
-		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
+		ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
 		request.setContent(content, contentTypeHeader);
 		return request;
 	}
@@ -175,34 +162,34 @@ public class SIPRequestHeaderProvider {
 	public Request createByteRequest(Device device, String channelId, SipTransactionInfo transactionInfo) throws ParseException, InvalidArgumentException, PeerUnavailableException {
 		Request request = null;
 		//请求行
-		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
+		SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
 		// via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
-		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
 		viaHeaders.add(viaHeader);
 		//from
-		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
-		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
-		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
+		SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
+		Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
 		//to
-		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,device.getHostAddress());
-		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
-		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,	transactionInfo.getToTag());
+		SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId,device.getHostAddress());
+		Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,	transactionInfo.getToTag());
 
 		//Forwards
-		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
 
 		//ceq
-		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
-		CallIdHeader callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
-		request = sipFactory.createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
+		CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.BYE);
+		CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
+		request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
 
-		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
 
-		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
-		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+		Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
+		request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
 
-		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
 
 		return request;
 	}
@@ -210,50 +197,50 @@ public class SIPRequestHeaderProvider {
 	public Request createSubscribeRequest(Device device, String content, SIPRequest requestOld, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
 		Request request = null;
 		// sipuri
-		SipURI requestURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
+		SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
 		// via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
-		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(),
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(),
 				device.getTransport(), SipUtils.getNewViaTag());
 		viaHeader.setRPort();
 		viaHeaders.add(viaHeader);
 		// from
-		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
-		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
-		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, requestOld == null ? SipUtils.getNewFromTag() :requestOld.getFromTag());
+		SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
+		Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, requestOld == null ? SipUtils.getNewFromTag() :requestOld.getFromTag());
 		// to
-		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
-		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
-		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, requestOld == null ? null :requestOld.getToTag());
+		SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
+		Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress, requestOld == null ? null :requestOld.getToTag());
 
 		// Forwards
-		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
 
 		// ceq
-		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.SUBSCRIBE);
+		CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.SUBSCRIBE);
 
-		request = sipFactory.createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader,
+		request = sipLayer.getSipFactory().createMessageFactory().createRequest(requestURI, Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader,
 				toHeader, viaHeaders, maxForwards);
 
 
-		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
-		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+		Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
+		request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
 
 		// Expires
-		ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires);
+		ExpiresHeader expireHeader = sipLayer.getSipFactory().createHeaderFactory().createExpiresHeader(expires);
 		request.addHeader(expireHeader);
 
 		// Event
-		EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader(event);
+		EventHeader eventHeader = sipLayer.getSipFactory().createHeaderFactory().createEventHeader(event);
 
 		int random = (int) Math.floor(Math.random() * 10000);
 		eventHeader.setEventId(random + "");
 		request.addHeader(eventHeader);
 
-		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
+		ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
 		request.setContent(content, contentTypeHeader);
 
-		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
 
 		return request;
 	}
@@ -265,64 +252,65 @@ public class SIPRequestHeaderProvider {
 		}
 		SIPRequest request = null;
 		//请求行
-		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
+		SipURI requestLine = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId, device.getHostAddress());
 		// via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
-		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(sipLayer.getLocalIp(device.getLocalIp()), sipConfig.getPort(), device.getTransport(), SipUtils.getNewViaTag());
 		viaHeaders.add(viaHeader);
 		//from
-		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
-		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
-		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
+		SipURI fromSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(),sipConfig.getDomain());
+		Address fromAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipLayer.getSipFactory().createHeaderFactory().createFromHeader(fromAddress, transactionInfo.getFromTag());
 		//to
-		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,device.getHostAddress());
-		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
-		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,	transactionInfo.getToTag());
+		SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(channelId,device.getHostAddress());
+		Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,	transactionInfo.getToTag());
 
 		//Forwards
-		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
 
 		//ceq
-		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INFO);
-		CallIdHeader callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
-		request = (SIPRequest)sipFactory.createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
+		CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(redisCatchStorage.getCSEQ(), Request.INFO);
+		CallIdHeader callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(transactionInfo.getCallId());
+		request = (SIPRequest)sipLayer.getSipFactory().createMessageFactory().createRequest(requestLine, Request.INFO, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
 
-		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
 
-		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
-		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+		Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), sipLayer.getLocalIp(device.getLocalIp())+":"+sipConfig.getPort()));
+		request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
 
-		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
 
 		if (content != null) {
-			ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application",
+			ContentTypeHeader contentTypeHeader = sipLayer.getSipFactory().createHeaderFactory().createContentTypeHeader("Application",
 					"MANSRTSP");
 			request.setContent(content, contentTypeHeader);
 		}
 		return request;
 	}
 
-	public Request createAckRequest(SipURI sipURI, SIPResponse sipResponse) throws ParseException, InvalidArgumentException, PeerUnavailableException {
+	public Request createAckRequest(String localIp, SipURI sipURI, SIPResponse sipResponse) throws ParseException, InvalidArgumentException, PeerUnavailableException {
+
 
 		// via
 		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
-		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), sipResponse.getTopmostViaHeader().getTransport(), SipUtils.getNewViaTag());
+		ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(localIp, sipConfig.getPort(), sipResponse.getTopmostViaHeader().getTransport(), SipUtils.getNewViaTag());
 		viaHeaders.add(viaHeader);
 
 		//Forwards
-		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
 
 		//ceq
-		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(sipResponse.getCSeqHeader().getSeqNumber(), Request.ACK);
+		CSeqHeader cSeqHeader = sipLayer.getSipFactory().createHeaderFactory().createCSeqHeader(sipResponse.getCSeqHeader().getSeqNumber(), Request.ACK);
 
-		Request request = sipFactory.createMessageFactory().createRequest(sipURI, Request.ACK, sipResponse.getCallIdHeader(), cSeqHeader, sipResponse.getFromHeader(), sipResponse.getToHeader(), viaHeaders, maxForwards);
+		Request request = sipLayer.getSipFactory().createMessageFactory().createRequest(sipURI, Request.ACK, sipResponse.getCallIdHeader(), cSeqHeader, sipResponse.getFromHeader(), sipResponse.getToHeader(), viaHeaders, maxForwards);
 
-		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
 
-		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
-		request.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+		Address concatAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(sipLayer.getSipFactory().createAddressFactory().createSipURI(sipConfig.getId(), localIp + ":"+sipConfig.getPort()));
+		request.addHeader(sipLayer.getSipFactory().createHeaderFactory().createContactHeader(concatAddress));
 
-		request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
+		request.addHeader(SipUtils.createUserAgentHeader(sipLayer.getSipFactory(), gitUtil));
 
 		return request;
 	}

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

@@ -1,17 +1,21 @@
 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
+import com.genersoft.iot.vmp.gb28181.SipLayer;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
+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.SIPRequestHeaderProvider;
+import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
+import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeFactory;
 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamChange;
 import com.genersoft.iot.vmp.media.zlm.dto.HookSubscribeForStreamPush;
@@ -23,16 +27,20 @@ import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
 import com.genersoft.iot.vmp.utils.GitUtil;
 import gov.nist.javax.sip.SipProviderImpl;
+import com.genersoft.iot.vmp.utils.DateUtil;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPResponse;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.DependsOn;
 import org.springframework.stereotype.Component;
 import org.springframework.util.ObjectUtils;
 
+import javax.sip.InvalidArgumentException;
+import javax.sip.ResponseEvent;
+import javax.sip.SipException;
+import javax.sip.header.CallIdHeader;
 import javax.sip.*;
 import javax.sip.header.*;
 import javax.sip.message.Request;
@@ -53,18 +61,10 @@ public class SIPCommander implements ISIPCommander {
     private SipConfig sipConfig;
 
     @Autowired
-    private SipFactory sipFactory;
-
-    @Autowired
-    private GitUtil gitUtil;
-
-    @Autowired
-    @Qualifier(value = "tcpSipProvider")
-    private SipProviderImpl tcpSipProvider;
+    private SipLayer sipLayer;
 
     @Autowired
-    @Qualifier(value = "udpSipProvider")
-    private SipProviderImpl udpSipProvider;
+    private SIPSender sipSender;
 
     @Autowired
     private SIPRequestHeaderProvider headerProvider;
@@ -78,8 +78,7 @@ public class SIPCommander implements ISIPCommander {
     @Autowired
     private ZlmHttpHookSubscribe subscribe;
 
-    @Autowired
-    private SipSubscribe sipSubscribe;
+
 
     @Autowired
     private IMediaServerService mediaServerService;
@@ -181,7 +180,7 @@ public class SIPCommander implements ISIPCommander {
     public void ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed,
                        int zoomSpeed) throws InvalidArgumentException, SipException, ParseException {
         String cmdStr = SipUtils.cmdString(leftRight, upDown, inOut, moveSpeed, zoomSpeed);
-        StringBuffer ptzXml = new StringBuffer(200);
+        StringBuilder ptzXml = new StringBuilder(200);
         String charset = device.getCharset();
         ptzXml.append("<?xml version=\"1.0\" encoding=\"" + charset + "\"?>\r\n");
         ptzXml.append("<Control>\r\n");
@@ -194,12 +193,9 @@ public class SIPCommander implements ISIPCommander {
         ptzXml.append("</Info>\r\n");
         ptzXml.append("</Control>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
-
-        Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
+        Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
 
-        transmitRequest(device.getTransport(), request);
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);
     }
 
     /**
@@ -230,11 +226,10 @@ public class SIPCommander implements ISIPCommander {
         ptzXml.append("</Control>\r\n");
 
 
-        CallIdHeader callIdHeader = device.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        SIPRequest request = (SIPRequest) headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-        transmitRequest(device.getTransport(), request);
+
+        SIPRequest request = (SIPRequest) headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);
 
     }
 
@@ -262,11 +257,8 @@ public class SIPCommander implements ISIPCommander {
         ptzXml.append("</Control>\r\n");
 
 
-        CallIdHeader callIdHeader = device.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
-
-        Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-        transmitRequest(device.getTransport(), request, errorEvent, okEvent);
+        Request request = headerProvider.createMessageRequest(device, ptzXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request, errorEvent, okEvent);
 
     }
 
@@ -295,12 +287,17 @@ public class SIPCommander implements ISIPCommander {
                 subscribe.removeSubscribe(hookSubscribe);
             }
         });
-        //
+        String sdpIp;
+        if (!ObjectUtils.isEmpty(device.getSdpIp())) {
+            sdpIp = device.getSdpIp();
+        }else {
+            sdpIp = mediaServerItem.getSdpIp();
+        }
         StringBuffer content = new StringBuffer(200);
         content.append("v=0\r\n");
-        content.append("o=" + channelId + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
+        content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n");
         content.append("s=Play\r\n");
-        content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
+        content.append("c=IN IP4 " + sdpIp + "\r\n");
         content.append("t=0 0\r\n");
 
         if (userSetting.isSeniorSdp()) {
@@ -353,11 +350,10 @@ public class SIPCommander implements ISIPCommander {
         // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率
 //			content.append("f=v/2/5/25/1/4000a/1/8/1" + "\r\n"); // 未发现支持此特性的设备
 
-        CallIdHeader callIdHeader = device.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(), callIdHeader);
-        transmitRequest(device.getTransport(), request, (e -> {
+
+        Request request = headerProvider.createInviteRequest(device, channelId, content.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, ssrcInfo.getSsrc(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, (e -> {
             streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
             mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
             errorEvent.response(e);
@@ -385,13 +381,18 @@ public class SIPCommander implements ISIPCommander {
 
 
         logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
-
+        String sdpIp;
+        if (!ObjectUtils.isEmpty(device.getSdpIp())) {
+            sdpIp = device.getSdpIp();
+        }else {
+            sdpIp = mediaServerItem.getSdpIp();
+        }
         StringBuffer content = new StringBuffer(200);
         content.append("v=0\r\n");
-        content.append("o=" + channelId + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
+        content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n");
         content.append("s=Playback\r\n");
         content.append("u=" + channelId + ":0\r\n");
-        content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
+        content.append("c=IN IP4 " + sdpIp + "\r\n");
         content.append("t=" + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime) + " "
                 + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) + "\r\n");
 
@@ -434,38 +435,39 @@ public class SIPCommander implements ISIPCommander {
             content.append("a=rtpmap:97 MPEG4/90000\r\n");
             content.append("a=rtpmap:98 H264/90000\r\n");
             content.append("a=rtpmap:99 H265/90000\r\n");
-            if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) { // tcp被动模式
+            if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) {
+                // tcp被动模式
                 content.append("a=setup:passive\r\n");
                 content.append("a=connection:new\r\n");
-            } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { // tcp主动模式
+            } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) {
+                // tcp主动模式
                 content.append("a=setup:active\r\n");
                 content.append("a=connection:new\r\n");
             }
         }
 
-        content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
+        //ssrc
+        content.append("y=" + ssrcInfo.getSsrc() + "\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
         HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
         // 添加订阅
         subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
             if (hookEvent != null) {
-                InviteStreamInfo inviteStreamInfo = new InviteStreamInfo(mediaServerItemInUse, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream());
+                InviteStreamInfo inviteStreamInfo = new InviteStreamInfo(mediaServerItemInUse, json,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream());
                 hookEvent.call(inviteStreamInfo);
             }
             subscribe.removeSubscribe(hookSubscribe);
         });
-        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader, ssrcInfo.getSsrc());
+        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc());
 
-        transmitRequest(device.getTransport(), request, errorEvent, event -> {
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> {
             ResponseEvent responseEvent = (ResponseEvent) event.event;
             SIPResponse response = (SIPResponse) responseEvent.getResponse();
-            streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.playback);
+            streamSession.put(device.getDeviceId(), channelId,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.playback);
             okEvent.response(event);
         });
         if (inviteStreamCallback != null) {
-            inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
+            inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream()));
         }
     }
 
@@ -484,13 +486,18 @@ public class SIPCommander implements ISIPCommander {
                                   SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
 
         logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
-
+        String sdpIp;
+        if (!ObjectUtils.isEmpty(device.getSdpIp())) {
+            sdpIp = device.getSdpIp();
+        }else {
+            sdpIp = mediaServerItem.getSdpIp();
+        }
         StringBuffer content = new StringBuffer(200);
         content.append("v=0\r\n");
-        content.append("o=" + channelId + " 0 0 IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
+        content.append("o=" + channelId + " 0 0 IN IP4 " + sdpIp + "\r\n");
         content.append("s=Download\r\n");
         content.append("u=" + channelId + ":0\r\n");
-        content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
+        content.append("c=IN IP4 " + sdpIp + "\r\n");
         content.append("t=" + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(startTime) + " "
                 + DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(endTime) + "\r\n");
 
@@ -546,13 +553,10 @@ public class SIPCommander implements ISIPCommander {
 
         content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
-
         HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId());
         // 添加订阅
         subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
-            hookEvent.call(new InviteStreamInfo(mediaServerItem, json, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
+            hookEvent.call(new InviteStreamInfo(mediaServerItem, json,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream()));
             subscribe.removeSubscribe(hookSubscribe);
             hookSubscribe.getContent().put("regist", false);
             hookSubscribe.getContent().put("schema", "rtsp");
@@ -561,7 +565,7 @@ public class SIPCommander implements ISIPCommander {
                     (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd) -> {
                         logger.info("[录像]下载结束, 发送BYE");
                         try {
-                            streamByeCmd(device, channelId, ssrcInfo.getStream(), callIdHeader.getCallId());
+                            streamByeCmd(device, channelId, ssrcInfo.getStream(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId());
                         } catch (InvalidArgumentException | ParseException | SipException |
                                  SsrcTransactionNotFoundException e) {
                             logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage());
@@ -569,14 +573,14 @@ public class SIPCommander implements ISIPCommander {
                     });
         });
 
-        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader, ssrcInfo.getSsrc());
+        Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc());
         if (inviteStreamCallback != null) {
-            inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null, callIdHeader.getCallId(), "rtp", ssrcInfo.getStream()));
+            inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream()));
         }
-        transmitRequest(device.getTransport(), request, errorEvent, okEvent -> {
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent -> {
             ResponseEvent responseEvent = (ResponseEvent) okEvent.event;
             SIPResponse response = (SIPResponse) responseEvent.getResponse();
-            streamSession.put(device.getDeviceId(), channelId, callIdHeader.getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download);
+            streamSession.put(device.getDeviceId(), channelId,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download);
         });
     }
 
@@ -665,7 +669,7 @@ public class SIPCommander implements ISIPCommander {
         streamSession.remove(ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getStream());
 
         Request byteRequest = headerProvider.createByteRequest(device, channelId, ssrcTransaction.getSipTransactionInfo());
-        transmitRequest(device.getTransport(), byteRequest, null, okEvent);
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), byteRequest, null, okEvent);
     }
 
     @Override
@@ -691,11 +695,10 @@ public class SIPCommander implements ISIPCommander {
         broadcastXml.append("<TargetID>" + channelId + "</TargetID>\r\n");
         broadcastXml.append("</Notify>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-        transmitRequest(device.getTransport(), request, errorEvent, okEvent);
+
+        Request request = headerProvider.createMessageRequest(device, broadcastXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request);
 
     }
 
@@ -723,11 +726,10 @@ public class SIPCommander implements ISIPCommander {
         cmdXml.append("<RecordCmd>" + recordCmdStr + "</RecordCmd>\r\n");
         cmdXml.append("</Control>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-        transmitRequest(device.getTransport(), request, errorEvent);
+
+        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);
     }
 
     /**
@@ -748,11 +750,10 @@ public class SIPCommander implements ISIPCommander {
         cmdXml.append("<TeleBoot>Boot</TeleBoot>\r\n");
         cmdXml.append("</Control>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-        transmitRequest(device.getTransport(), request);
+
+        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);
     }
 
     /**
@@ -774,11 +775,10 @@ public class SIPCommander implements ISIPCommander {
         cmdXml.append("<GuardCmd>" + guardCmdStr + "</GuardCmd>\r\n");
         cmdXml.append("</Control>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-        transmitRequest(device.getTransport(), request, errorEvent);
+
+        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);
     }
 
     /**
@@ -811,11 +811,10 @@ public class SIPCommander implements ISIPCommander {
         }
         cmdXml.append("</Control>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-        transmitRequest(device.getTransport(), request, errorEvent);
+
+        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);
     }
 
     /**
@@ -841,11 +840,10 @@ public class SIPCommander implements ISIPCommander {
         cmdXml.append("<IFameCmd>Send</IFameCmd>\r\n");
         cmdXml.append("</Control>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-        transmitRequest(device.getTransport(), request);
+
+        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);
     }
 
     /**
@@ -889,11 +887,10 @@ public class SIPCommander implements ISIPCommander {
         cmdXml.append("</HomePosition>\r\n");
         cmdXml.append("</Control>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-        transmitRequest(device.getTransport(), request, errorEvent);
+
+        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);
     }
 
     /**
@@ -953,11 +950,10 @@ public class SIPCommander implements ISIPCommander {
         cmdXml.append("</BasicParam>\r\n");
         cmdXml.append("</Control>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-        transmitRequest(device.getTransport(), request, errorEvent);
+
+        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);
     }
 
     /**
@@ -977,12 +973,11 @@ public class SIPCommander implements ISIPCommander {
         catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
         catalogXml.append("</Query>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
 
-        transmitRequest(device.getTransport(), request, errorEvent);
+        Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
+
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
     }
 
     /**
@@ -1002,12 +997,11 @@ public class SIPCommander implements ISIPCommander {
         catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
         catalogXml.append("</Query>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
 
-        transmitRequest(device.getTransport(), request);
+        Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
+
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request);
 
     }
 
@@ -1028,12 +1022,11 @@ public class SIPCommander implements ISIPCommander {
         catalogXml.append("  <DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
         catalogXml.append("</Query>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
 
-        transmitRequest(device.getTransport(), request, errorEvent);
+        Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
+
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
     }
 
     /**
@@ -1074,13 +1067,12 @@ public class SIPCommander implements ISIPCommander {
         }
         recordInfoXml.append("</Query>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
+
 
         Request request = headerProvider.createMessageRequest(device, recordInfoXml.toString(),
-                SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
+                SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
 
-        transmitRequest(device.getTransport(), request, errorEvent, okEvent);
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
     }
 
     /**
@@ -1126,11 +1118,10 @@ public class SIPCommander implements ISIPCommander {
         }
         cmdXml.append("</Query>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-        transmitRequest(device.getTransport(), request, errorEvent);
+
+        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);
     }
 
     /**
@@ -1157,11 +1148,10 @@ public class SIPCommander implements ISIPCommander {
         cmdXml.append("<ConfigType>" + configType + "</ConfigType>\r\n");
         cmdXml.append("</Query>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-        transmitRequest(device.getTransport(), request, errorEvent);
+
+        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);
     }
 
     /**
@@ -1185,11 +1175,10 @@ public class SIPCommander implements ISIPCommander {
         }
         cmdXml.append("</Query>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null, callIdHeader);
-        transmitRequest(device.getTransport(), request, errorEvent);
+
+        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);
     }
 
     /**
@@ -1210,12 +1199,11 @@ public class SIPCommander implements ISIPCommander {
         mobilePostitionXml.append("<Interval>60</Interval>\r\n");
         mobilePostitionXml.append("</Query>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
 
-        transmitRequest(device.getTransport(), request, errorEvent);
+        Request request = headerProvider.createMessageRequest(device, mobilePostitionXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
+
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
 
     }
 
@@ -1243,14 +1231,13 @@ public class SIPCommander implements ISIPCommander {
         CallIdHeader callIdHeader;
 
         if (requestOld != null) {
-            callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
+            callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
         } else {
-            callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                    : udpSipProvider.getNewCallId();
+            callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport());
         }
-        SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), requestOld, device.getSubscribeCycleForMobilePosition(), "presence", callIdHeader); //Position;id=" + tm.substring(tm.length() - 4));
+        SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), requestOld, device.getSubscribeCycleForMobilePosition(), "presence",callIdHeader); //Position;id=" + tm.substring(tm.length() - 4));
 
-        transmitRequest(device.getTransport(), request, errorEvent, okEvent);
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
         return request;
     }
 
@@ -1297,11 +1284,10 @@ public class SIPCommander implements ISIPCommander {
         }
         cmdXml.append("</Query>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
 
-        Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), null, expires, "presence", callIdHeader);
-        transmitRequest(device.getTransport(), request);
+
+        Request request = headerProvider.createSubscribeRequest(device, cmdXml.toString(), null, expires, "presence",sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request);
 
     }
 
@@ -1320,16 +1306,15 @@ public class SIPCommander implements ISIPCommander {
         CallIdHeader callIdHeader;
 
         if (requestOld != null) {
-            callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
+            callIdHeader = sipLayer.getSipFactory().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId());
         } else {
-            callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                    : udpSipProvider.getNewCallId();
+            callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport());
         }
 
         // 有效时间默认为60秒以上
         SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, cmdXml.toString(), requestOld, device.getSubscribeCycleForCatalog(), "Catalog",
                 callIdHeader);
-        transmitRequest(device.getTransport(), request, errorEvent, okEvent);
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
         return request;
     }
 
@@ -1349,59 +1334,14 @@ public class SIPCommander implements ISIPCommander {
         }
         dragXml.append(cmdString);
         dragXml.append("</Control>\r\n");
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
-        Request request = headerProvider.createMessageRequest(device, dragXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-        logger.debug("拉框信令: " + request.toString());
-        transmitRequest(device.getTransport(), request);
-    }
-
-
-    @Override
-    public void transmitRequest(String transport, Request request) throws SipException, ParseException {
-        transmitRequest(transport, request, null, null);
-    }
 
-    @Override
-    public void transmitRequest(String transport, Request request, SipSubscribe.Event errorEvent) throws SipException, ParseException {
-        transmitRequest(transport, request, errorEvent, null);
+        Request request = headerProvider.createMessageRequest(device, dragXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
+        logger.debug("拉框信令: " + request.toString());
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);
     }
 
-    @Override
-    public void transmitRequest(String transport, Request request, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, ParseException {
-
-        if (request.getHeader(UserAgentHeader.NAME) == null) {
-            try {
-                request.addHeader(SipUtils.createUserAgentHeader(sipFactory, gitUtil));
-            } catch (ParseException e) {
-                logger.error("添加UserAgentHeader失败", e);
-            }
-        }
 
-        CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
-        // 添加错误订阅
-        if (errorEvent != null) {
-            sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (eventResult -> {
-                errorEvent.response(eventResult);
-                sipSubscribe.removeErrorSubscribe(eventResult.callId);
-                sipSubscribe.removeOkSubscribe(eventResult.callId);
-            }));
-        }
-        // 添加订阅
-        if (okEvent != null) {
-            sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), eventResult -> {
-                okEvent.response(eventResult);
-                sipSubscribe.removeOkSubscribe(eventResult.callId);
-                sipSubscribe.removeErrorSubscribe(eventResult.callId);
-            });
-        }
-        if ("TCP".equals(transport)) {
-            tcpSipProvider.sendRequest(request);
-        } else if ("UDP".equals(transport)) {
-            udpSipProvider.sendRequest(request);
-        }
 
-    }
 
 
     /**
@@ -1476,7 +1416,7 @@ public class SIPCommander implements ISIPCommander {
             return;
         }
 
-        transmitRequest(device.getTransport(), request, errorEvent, okEvent);
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
     }
 
     @Override
@@ -1505,10 +1445,9 @@ public class SIPCommander implements ISIPCommander {
         deviceStatusXml.append("</info>\r\n");
         deviceStatusXml.append("</Notify>\r\n");
 
-        CallIdHeader callIdHeader = device.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
-        Request request = headerProvider.createMessageRequest(device, deviceStatusXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null, callIdHeader);
-        transmitRequest(device.getTransport(), request);
+
+        Request request = headerProvider.createMessageRequest(device, deviceStatusXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
+        sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);
 
 
     }

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

@@ -1,8 +1,11 @@
 package com.genersoft.iot.vmp.gb28181.transmit.cmd.impl;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.genersoft.iot.vmp.gb28181.SipLayer;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderPlarformProvider;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
@@ -13,15 +16,12 @@ import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-import gov.nist.javax.sip.SipProviderImpl;
 import gov.nist.javax.sip.message.MessageFactoryImpl;
 import gov.nist.javax.sip.message.SIPRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.DependsOn;
-import org.springframework.context.annotation.Lazy;
 import org.springframework.lang.Nullable;
 import org.springframework.stereotype.Component;
 import org.springframework.util.ObjectUtils;
@@ -56,21 +56,11 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
     @Autowired
     private ZLMRTPServerFactory zlmrtpServerFactory;
 
-    @Lazy
     @Autowired
-    @Qualifier(value="tcpSipProvider")
-    private SipProviderImpl tcpSipProvider;
+    private SipLayer sipLayer;
 
-    @Lazy
     @Autowired
-    @Qualifier(value="udpSipProvider")
-    private SipProviderImpl udpSipProvider;
-
-    @Autowired
-    private SipFactory sipFactory;
-
-    @Autowired
-    private SubscribeHolder subscribeHolder;
+    private SIPSender sipSender;
 
     @Override
     public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
@@ -87,13 +77,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
                             SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException {
             Request request;
             if (!registerAgain ) {
-                CallIdHeader callIdHeader = null;
-                if(parentPlatform.getTransport().equalsIgnoreCase("TCP")) {
-                    callIdHeader = tcpSipProvider.getNewCallId();
-                }
-                if(parentPlatform.getTransport().equalsIgnoreCase("UDP")) {
-                    callIdHeader = udpSipProvider.getNewCallId();
-                }
+                CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
 
                 request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform,
                         redisCatchStorage.getCSEQ(), SipUtils.getNewFromTag(),
@@ -115,12 +99,11 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
                 });
 
             }else {
-                CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                        : udpSipProvider.getNewCallId();
+                CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
                 request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, SipUtils.getNewFromTag(), null, callId, www, callIdHeader, isRegister);
             }
 
-            transmitRequest(parentPlatform, request, null, okEvent);
+            sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, okEvent);
     }
 
     @Override
@@ -135,8 +118,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
             keepaliveXml.append("<Status>OK</Status>\r\n");
             keepaliveXml.append("</Notify>\r\n");
 
-            CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                    : udpSipProvider.getNewCallId();
+        CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
 
             Request request = headerProviderPlatformProvider.createMessageRequest(
                     parentPlatform,
@@ -144,39 +126,10 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
                     SipUtils.getNewFromTag(),
                     SipUtils.getNewViaTag(),
                     callIdHeader);
-            transmitRequest(parentPlatform, request, errorEvent, okEvent);
+            sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, errorEvent, okEvent);
         return callIdHeader.getCallId();
     }
 
-    private void transmitRequest(ParentPlatform parentPlatform, Request request) throws SipException {
-        transmitRequest(parentPlatform, request, null, null);
-    }
-
-    private void transmitRequest(ParentPlatform parentPlatform, Request request, SipSubscribe.Event errorEvent) throws SipException {
-        transmitRequest(parentPlatform, request, errorEvent, null);
-    }
-
-    private void transmitRequest(ParentPlatform parentPlatform, Request request, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws SipException {
-        logger.debug("\n发送消息:\n{}", request);
-        if("TCP".equalsIgnoreCase(parentPlatform.getTransport())) {
-            tcpSipProvider.sendRequest(request);
-
-        } else if("UDP".equalsIgnoreCase(parentPlatform.getTransport())) {
-            udpSipProvider.sendRequest(request);
-        }
-
-        CallIdHeader callIdHeader = (CallIdHeader)request.getHeader(CallIdHeader.NAME);
-        // 添加错误订阅
-        if (errorEvent != null) {
-            sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), errorEvent);
-        }
-        // 添加订阅
-        if (okEvent != null) {
-            sipSubscribe.addOkSubscribe(callIdHeader.getCallId(), okEvent);
-        }
-
-    }
-
     /**
      * 向上级回复通道信息
      * @param channel 通道信息
@@ -196,11 +149,10 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         String catalogXml = getCatalogXml(channels, sn, parentPlatform, size);
 
         // callid
-        CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
+        CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
 
         Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
-        transmitRequest(parentPlatform, request);
+        sipSender.transmitRequest(parentPlatform.getDeviceIp(), request);
 
     }
 
@@ -283,11 +235,10 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         }
         String catalogXml = getCatalogXml(deviceChannels, sn, parentPlatform, channels.size());
         // callid
-        CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
+        CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
 
         Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, catalogXml, fromTag, SipUtils.getNewViaTag(), callIdHeader);
-        transmitRequest(parentPlatform, request, null, eventResult -> {
+        sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, eventResult -> {
             int indexNext = index + parentPlatform.getCatalogGroup();
             try {
                 sendCatalogResponse(channels, parentPlatform, sn, fromTag, indexNext);
@@ -323,11 +274,10 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         deviceInfoXml.append("<Result>OK</Result>\r\n");
         deviceInfoXml.append("</Response>\r\n");
 
-        CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
+        CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
 
         Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceInfoXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
-        transmitRequest(parentPlatform, request);
+        sipSender.transmitRequest(parentPlatform.getDeviceIp(), request);
     }
 
     /**
@@ -354,11 +304,10 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         deviceStatusXml.append("<Status>OK</Status>\r\n");
         deviceStatusXml.append("</Response>\r\n");
 
-        CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
+        CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
 
         Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceStatusXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
-        transmitRequest(parentPlatform, request);
+        sipSender.transmitRequest(parentPlatform.getDeviceIp(), request);
 
     }
 
@@ -398,7 +347,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
             return;
         }
         logger.info("[发送报警通知] {}/{}->{},{}: {}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(),
-                deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSONObject.toJSON(deviceAlarm));
+                deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSON.toJSONString(deviceAlarm));
         String characterSet = parentPlatform.getCharacterSet();
         StringBuffer deviceStatusXml = new StringBuffer(600);
         deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
@@ -417,11 +366,10 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         deviceStatusXml.append("</info>\r\n");
         deviceStatusXml.append("</Notify>\r\n");
 
-        CallIdHeader callIdHeader = parentPlatform.getTransport().equalsIgnoreCase("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
+        CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
 
         Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, deviceStatusXml.toString(), SipUtils.getNewFromTag(), SipUtils.getNewViaTag(), callIdHeader);
-        transmitRequest(parentPlatform, request);
+        sipSender.transmitRequest(parentPlatform.getDeviceIp(), request);
 
     }
 
@@ -461,14 +409,14 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
     private void sendNotify(ParentPlatform parentPlatform, String catalogXmlContent,
                                    SubscribeInfo subscribeInfo, SipSubscribe.Event errorEvent,  SipSubscribe.Event okEvent )
             throws SipException, ParseException, InvalidArgumentException {
-		MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipFactory.createMessageFactory();
+		MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipLayer.getSipFactory().createMessageFactory();
         String characterSet = parentPlatform.getCharacterSet();
  		// 设置编码, 防止中文乱码
 		messageFactory.setDefaultContentEncodingCharset(characterSet);
 
         SIPRequest notifyRequest = headerProviderPlatformProvider.createNotifyRequest(parentPlatform, catalogXmlContent, subscribeInfo);
 
-        transmitRequest(parentPlatform, notifyRequest);
+        sipSender.transmitRequest(parentPlatform.getDeviceIp(), notifyRequest);
     }
 
     private  String getCatalogXmlContentForCatalogAddOrUpdate(ParentPlatform parentPlatform, List<DeviceChannel> channels, int sumNum, String type, SubscribeInfo subscribeInfo) {
@@ -633,21 +581,21 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         recordXml.append("</Response>\r\n");
 
         // callid
-        CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
-                : udpSipProvider.getNewCallId();
+        CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
+
         Request request = headerProviderPlatformProvider.createMessageRequest(parentPlatform, recordXml.toString(), fromTag, SipUtils.getNewViaTag(), callIdHeader);
-        transmitRequest(parentPlatform, request);
+        sipSender.transmitRequest(parentPlatform.getDeviceIp(), request);
 
     }
 
     @Override
-    public void sendMediaStatusNotify(ParentPlatform platform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException {
-        if (sendRtpItem == null || platform == null) {
+    public void sendMediaStatusNotify(ParentPlatform parentPlatform, SendRtpItem sendRtpItem) throws SipException, InvalidArgumentException, ParseException {
+        if (sendRtpItem == null || parentPlatform == null) {
             return;
         }
 
 
-        String characterSet = platform.getCharacterSet();
+        String characterSet = parentPlatform.getCharacterSet();
         StringBuffer mediaStatusXml = new StringBuffer(200);
         mediaStatusXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
         mediaStatusXml.append("<Notify>\r\n");
@@ -657,10 +605,10 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
         mediaStatusXml.append("<NotifyType>121</NotifyType>\r\n");
         mediaStatusXml.append("</Notify>\r\n");
 
-        SIPRequest messageRequest = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(platform, mediaStatusXml.toString(),
+        SIPRequest messageRequest = (SIPRequest)headerProviderPlatformProvider.createMessageRequest(parentPlatform, mediaStatusXml.toString(),
                 sendRtpItem);
 
-        transmitRequest(platform, messageRequest);
+        sipSender.transmitRequest(parentPlatform.getDeviceIp(),messageRequest);
 
     }
 
@@ -681,21 +629,21 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
             logger.info("[向上级发送BYE], sendRtpItem 为NULL");
             return;
         }
-        if (platform == null) {
+        if (parentPlatform == null) {
             logger.info("[向上级发送BYE], platform 为NULL");
             return;
         }
-        logger.info("[向上级发送BYE], {}/{}", platform.getServerGBId(), sendRtpItem.getChannelId());
+        logger.info("[向上级发送BYE], {}/{}", parentPlatform.getServerGBId(), sendRtpItem.getChannelId());
         String mediaServerId = sendRtpItem.getMediaServerId();
         MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
         if (mediaServerItem != null) {
             mediaServerService.releaseSsrc(mediaServerItem.getId(), sendRtpItem.getSsrc());
-            zlmrtpServerFactory.closeRTPServer(mediaServerItem, sendRtpItem.getStreamId());
+            zlmrtpServerFactory.closeRtpServer(mediaServerItem, sendRtpItem.getStreamId());
         }
-        SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(platform, sendRtpItem);
+        SIPRequest byeRequest = headerProviderPlatformProvider.createByeRequest(parentPlatform, sendRtpItem);
         if (byeRequest == null) {
             logger.warn("[向上级发送bye]:无法创建 byeRequest");
         }
-        transmitRequest(platform,byeRequest);
+        sipSender.transmitRequest(parentPlatform.getDeviceIp(),byeRequest);
     }
 }

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

@@ -2,9 +2,9 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request;
 
 import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
 import gov.nist.javax.sip.SipProviderImpl;
-import gov.nist.javax.sip.SipStackImpl;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPResponse;
 import gov.nist.javax.sip.stack.SIPServerTransactionImpl;
@@ -42,58 +42,8 @@ public abstract class SIPRequestProcessorParent {
 	private final static Logger logger = LoggerFactory.getLogger(SIPRequestProcessorParent.class);
 
 	@Autowired
-	@Qualifier(value="tcpSipProvider")
-	private SipProviderImpl tcpSipProvider;
+	private SIPSender sipSender;
 
-	@Autowired
-	@Qualifier(value="udpSipProvider")
-	private SipProviderImpl udpSipProvider;
-
-	@Autowired
-	private SipConfig sipConfig;
-
-	/**
-	 * 根据 RequestEvent 获取 ServerTransaction
-	 * @param evt
-	 * @return
-	 */
-	public ServerTransaction getServerTransaction(RequestEvent evt) {
-		Request request = evt.getRequest();
-		SIPServerTransactionImpl serverTransaction = (SIPServerTransactionImpl)evt.getServerTransaction();
-		// 判断TCP还是UDP
-		boolean isTcp = false;
-		ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
-		String transport = reqViaHeader.getTransport();
-		if (transport.equalsIgnoreCase("TCP")) {
-			isTcp = true;
-		}
-		if (serverTransaction != null && serverTransaction.getOriginalRequest() == null) {
-			serverTransaction.setOriginalRequest((SIPRequest) evt.getRequest());
-		}
-		if (serverTransaction == null) {
-			try {
-				if (isTcp) {
-					SipStackImpl stack = (SipStackImpl)tcpSipProvider.getSipStack();
-					serverTransaction = (SIPServerTransactionImpl) stack.findTransaction((SIPRequest)request, true);
-					if (serverTransaction == null) {
-						serverTransaction = (SIPServerTransactionImpl)tcpSipProvider.getNewServerTransaction(request);
-					}
-				} else {
-					SipStackImpl stack = (SipStackImpl)udpSipProvider.getSipStack();
-					serverTransaction = (SIPServerTransactionImpl) stack.findTransaction((SIPRequest)request, true);
-					if (serverTransaction == null) {
-						serverTransaction = (SIPServerTransactionImpl)udpSipProvider.getNewServerTransaction(request);
-					}
-				}
-			} catch (TransactionAlreadyExistsException e) {
-				logger.error(e.getMessage());
-			} catch (TransactionUnavailableException e) {
-				logger.error(e.getMessage());
-			}
-		}
-		return serverTransaction;
-	}
-	
 	public AddressFactory getAddressFactory() {
 		try {
 			return SipFactory.getInstance().createAddressFactory();
@@ -135,25 +85,73 @@ public abstract class SIPRequestProcessorParent {
 	 * 400
 	 * 404
 	 */
-	public SIPResponse responseAck(ServerTransaction serverTransaction, int statusCode) throws SipException, InvalidArgumentException, ParseException {
-		return responseAck(serverTransaction, statusCode, null);
+	public SIPResponse responseAck(SIPRequest sipRequest, int statusCode) throws SipException, InvalidArgumentException, ParseException {
+		return responseAck(sipRequest, statusCode, null);
 	}
 
-	public SIPResponse responseAck(ServerTransaction serverTransaction, int statusCode, String msg) throws SipException, InvalidArgumentException, ParseException {
-		return responseAck(serverTransaction, statusCode, msg, null);
+	public SIPResponse responseAck(SIPRequest sipRequest, int statusCode, String msg) throws SipException, InvalidArgumentException, ParseException {
+		return responseAck(sipRequest, statusCode, msg, null);
 	}
 
-	public SIPResponse responseAck(ServerTransaction serverTransaction, int statusCode, String msg, ResponseAckExtraParam responseAckExtraParam) throws SipException, InvalidArgumentException, ParseException {
-		ToHeader toHeader = (ToHeader) serverTransaction.getRequest().getHeader(ToHeader.NAME);
-		if (toHeader.getTag() == null) {
-			toHeader.setTag(SipUtils.getNewTag());
+//	public SIPResponse responseAck(ServerTransaction serverTransaction, int statusCode, String msg, ResponseAckExtraParam responseAckExtraParam) throws SipException, InvalidArgumentException, ParseException {
+//		if (serverTransaction == null) {
+//			logger.warn("[回复消息] ServerTransaction 为null");
+//			return null;
+//		}
+//		ToHeader toHeader = (ToHeader) serverTransaction.getRequest().getHeader(ToHeader.NAME);
+//		if (toHeader.getTag() == null) {
+//			toHeader.setTag(SipUtils.getNewTag());
+//		}
+//		SIPResponse response = (SIPResponse)getMessageFactory().createResponse(statusCode, serverTransaction.getRequest());
+//		if (msg != null) {
+//			response.setReasonPhrase(msg);
+//		}
+//		if (responseAckExtraParam != null) {
+//			if (responseAckExtraParam.sipURI != null && serverTransaction.getRequest().getMethod().equals(Request.INVITE)) {
+//				logger.debug("responseSdpAck SipURI: {}:{}", responseAckExtraParam.sipURI.getHost(), responseAckExtraParam.sipURI.getPort());
+//				Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(
+//						SipFactory.getInstance().createAddressFactory().createSipURI(responseAckExtraParam.sipURI.getUser(),  responseAckExtraParam.sipURI.getHost()+":"+responseAckExtraParam.sipURI.getPort()
+//						));
+//				response.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress));
+//			}
+//			if (responseAckExtraParam.contentTypeHeader != null) {
+//				response.setContent(responseAckExtraParam.content, responseAckExtraParam.contentTypeHeader);
+//			}
+//
+//			if (serverTransaction.getRequest().getMethod().equals(Request.SUBSCRIBE)) {
+//				if (responseAckExtraParam.expires == -1) {
+//					logger.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header");
+//				}else {
+//					ExpiresHeader expiresHeader = SipFactory.getInstance().createHeaderFactory().createExpiresHeader(responseAckExtraParam.expires);
+//					response.addHeader(expiresHeader);
+//				}
+//			}
+//		}else {
+//			if (serverTransaction.getRequest().getMethod().equals(Request.SUBSCRIBE)) {
+//				logger.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header");
+//			}
+//		}
+//		serverTransaction.sendResponse(response);
+//		if (statusCode >= 200 && !"NOTIFY".equalsIgnoreCase(serverTransaction.getRequest().getMethod())) {
+//			if (serverTransaction.getDialog() != null) {
+//				serverTransaction.getDialog().delete();
+//			}
+//		}
+//		return response;
+//	}
+
+	public SIPResponse responseAck(SIPRequest sipRequest, int statusCode, String msg, ResponseAckExtraParam responseAckExtraParam) throws SipException, InvalidArgumentException, ParseException {
+		if (sipRequest.getToHeader().getTag() == null) {
+			sipRequest.getToHeader().setTag(SipUtils.getNewTag());
 		}
-		SIPResponse response = (SIPResponse)getMessageFactory().createResponse(statusCode, serverTransaction.getRequest());
+		SIPResponse response = (SIPResponse)getMessageFactory().createResponse(statusCode, sipRequest);
+		response.setStatusCode(statusCode);
 		if (msg != null) {
 			response.setReasonPhrase(msg);
 		}
+
 		if (responseAckExtraParam != null) {
-			if (responseAckExtraParam.sipURI != null && serverTransaction.getRequest().getMethod().equals(Request.INVITE)) {
+			if (responseAckExtraParam.sipURI != null && sipRequest.getMethod().equals(Request.INVITE)) {
 				logger.debug("responseSdpAck SipURI: {}:{}", responseAckExtraParam.sipURI.getHost(), responseAckExtraParam.sipURI.getPort());
 				Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(
 						SipFactory.getInstance().createAddressFactory().createSipURI(responseAckExtraParam.sipURI.getUser(),  responseAckExtraParam.sipURI.getHost()+":"+responseAckExtraParam.sipURI.getPort()
@@ -164,7 +162,7 @@ public abstract class SIPRequestProcessorParent {
 				response.setContent(responseAckExtraParam.content, responseAckExtraParam.contentTypeHeader);
 			}
 
-			if (serverTransaction.getRequest().getMethod().equals(Request.SUBSCRIBE)) {
+			if (sipRequest.getMethod().equals(Request.SUBSCRIBE)) {
 				if (responseAckExtraParam.expires == -1) {
 					logger.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header");
 				}else {
@@ -173,27 +171,26 @@ public abstract class SIPRequestProcessorParent {
 				}
 			}
 		}else {
-			if (serverTransaction.getRequest().getMethod().equals(Request.SUBSCRIBE)) {
+			if (sipRequest.getMethod().equals(Request.SUBSCRIBE)) {
 				logger.error("[参数不全] 2xx的SUBSCRIBE回复,必须设置Expires header");
 			}
 		}
-		serverTransaction.sendResponse(response);
-		if (statusCode >= 200 && !"NOTIFY".equalsIgnoreCase(serverTransaction.getRequest().getMethod())) {
-			if (serverTransaction.getDialog() != null) {
-				serverTransaction.getDialog().delete();
-			}
-		}
+
+		// 发送response
+		sipSender.transmitRequest(sipRequest.getLocalAddress().getHostAddress(), response);
+
 		return response;
 	}
 
 	/**
 	 * 回复带sdp的200
 	 */
-	public SIPResponse responseSdpAck(ServerTransaction serverTransaction, String sdp, ParentPlatform platform) throws SipException, InvalidArgumentException, ParseException {
+	public SIPResponse responseSdpAck(SIPRequest request, String sdp, ParentPlatform platform) throws SipException, InvalidArgumentException, ParseException {
+
 		ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
 
 		// 兼容国标中的使用编码@域名作为RequestURI的情况
-		SipURI sipURI = (SipURI)serverTransaction.getRequest().getRequestURI();
+		SipURI sipURI = (SipURI)request.getRequestURI();
 		if (sipURI.getPort() == -1) {
 			sipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(),  platform.getServerIP()+":"+platform.getServerPort());
 		}
@@ -202,16 +199,16 @@ public abstract class SIPRequestProcessorParent {
 		responseAckExtraParam.content = sdp;
 		responseAckExtraParam.sipURI = sipURI;
 
-		return responseAck(serverTransaction, Response.OK, null, responseAckExtraParam);
+		return responseAck(request, Response.OK, null, responseAckExtraParam);
 	}
 
 	/**
 	 * 回复带xml的200
 	 */
-	public SIPResponse responseXmlAck(ServerTransaction serverTransaction, String xml, ParentPlatform platform, Integer expires) throws SipException, InvalidArgumentException, ParseException {
+	public SIPResponse responseXmlAck(SIPRequest request, String xml, ParentPlatform platform, Integer expires) throws SipException, InvalidArgumentException, ParseException {
 		ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
 
-		SipURI sipURI = (SipURI)serverTransaction.getRequest().getRequestURI();
+		SipURI sipURI = (SipURI)request.getRequestURI();
 		if (sipURI.getPort() == -1) {
 			sipURI = SipFactory.getInstance().createAddressFactory().createSipURI(platform.getServerGBId(),  platform.getServerIP()+":"+platform.getServerPort());
 		}
@@ -220,7 +217,7 @@ public abstract class SIPRequestProcessorParent {
 		responseAckExtraParam.content = xml;
 		responseAckExtraParam.sipURI = sipURI;
 		responseAckExtraParam.expires = expires;
-		return responseAck(serverTransaction, Response.OK, null, responseAckExtraParam);
+		return responseAck(request, Response.OK, null, responseAckExtraParam);
 	}
 
 	public Element getRootElement(RequestEvent evt) throws DocumentException {

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

@@ -1,6 +1,7 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
 import com.genersoft.iot.vmp.gb28181.bean.*;
@@ -12,8 +13,8 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
-import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
+import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.service.IMediaServerService;
@@ -40,7 +41,8 @@ import javax.sip.header.HeaderAddress;
 import java.text.ParseException;
 import javax.sip.header.ToHeader;
 import java.text.ParseException;
-import java.util.*;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * SIP命令类型: ACK请求
@@ -70,6 +72,9 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
 	@Autowired
 	private ZLMRTPServerFactory zlmrtpServerFactory;
 
+	@Autowired
+	private ZlmHttpHookSubscribe hookSubscribe;
+
 	@Autowired
 	private IMediaServerService mediaServerService;
 
@@ -177,8 +182,18 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
 						startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, json, param, callIdHeader);
 					});
 				} else {
-					JSONObject startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
-					startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, startSendRtpStreamResult, param, callIdHeader);
+					// 如果是非严格模式,需要关闭端口占用
+					JSONObject startSendRtpStreamResult = null;
+					if (sendRtpItem.getLocalPort() != 0) {
+						if (zlmrtpServerFactory.releasePort(mediaInfo, sendRtpItem.getSsrc())) {
+							startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
+						}
+					}else {
+						startSendRtpStreamResult = zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
+					}
+					if (startSendRtpStreamResult != null) {
+						startSendRtpStreamHand(evt, sendRtpItem, parentPlatform, startSendRtpStreamResult, param, callIdHeader);
+					}
 				}
 
 
@@ -192,7 +207,7 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
 			logger.info("调用ZLM推流接口, 结果: {}",  jsonObject);
 			logger.info("RTP推流成功[ {}/{} ],{}->{}:{}, " ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"), param.get("dst_url"), param.get("dst_port"));
 		} else {
-			logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"),JSONObject.toJSON(param));
+			logger.error("RTP推流失败: {}, 参数:{}",jsonObject.getString("msg"), JSON.toJSONString(param));
 			if (sendRtpItem.isOnlyAudio()) {
 				// TODO 可能是语音对讲
 			}else {

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

@@ -17,6 +17,7 @@ import com.genersoft.iot.vmp.service.IPlayService;
 import com.genersoft.iot.vmp.service.bean.MessageForPushChannel;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
@@ -87,7 +88,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
 	public void process(RequestEvent evt) {
 
 		try {
-			responseAck(getServerTransaction(evt), Response.OK);
+			responseAck((SIPRequest) evt.getRequest(), Response.OK);
 		} catch (SipException | InvalidArgumentException | ParseException e) {
 			logger.error("[回复BYE信息失败],{}", e.getMessage());
 		}

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

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.conf.UserSetting;
@@ -15,13 +15,15 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 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.impl.SIPCommander;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
-import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.ZLMMediaListManager;
 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
+import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.dto.*;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IMediaService;
@@ -48,12 +50,14 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import javax.sdp.*;
-import javax.sip.*;
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
 import javax.sip.header.CallIdHeader;
-import javax.sip.message.Request;
 import javax.sip.message.Response;
 import java.text.ParseException;
 import java.time.Instant;
+import java.util.Random;
 import java.util.Vector;
 
 /**
@@ -92,7 +96,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
     private IPlayService playService;
 
     @Autowired
-    private ISIPCommander commander;
+    private SIPSender sipSender;
 
 	@Autowired
 	private AudioBroadcastManager audioBroadcastManager;
@@ -154,16 +158,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
     public void process(RequestEvent evt) {
         //  Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令
         try {
-            Request request = evt.getRequest();
+            SIPRequest request = (SIPRequest)evt.getRequest();
             String channelId = SipUtils.getChannelIdFromRequest(request);
             String requesterId = SipUtils.getUserIdFromFromHeader(request);
             CallIdHeader callIdHeader = (CallIdHeader) request.getHeader(CallIdHeader.NAME);
-            ServerTransaction serverTransaction = getServerTransaction(evt);
-            if (requesterId == null) {
-                logger.info("无法从FromHeader的Address中获取到平台/设备id,返回400");
+            if (requesterId == null || channelId == null) {
+                logger.info("无法从FromHeader的Address中获取到平台id,返回400");
                 // 参数不全, 发400,请求错误
                 try {
-                    responseAck(serverTransaction, Response.BAD_REQUEST);
+                    responseAck(request, Response.BAD_REQUEST);
                 } catch (SipException | InvalidArgumentException | ParseException e) {
                     logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage());
                 }
@@ -195,7 +198,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
             // 查询请求是否来自上级平台\设备
             ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);
             if (platform == null) {
-                inviteFromDeviceHandle(serverTransaction, requesterId, channelId);
+                inviteFromDeviceHandle(request, requesterId, channelId);
+
             } else {
                 // 查询平台下是否有该通道
                 DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);
@@ -207,14 +211,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                 StreamProxyItem proxyByAppAndStream =null;
                 // 不是通道可能是直播流
                 if (channel != null && gbStream == null) {
-//                    if (channel.getStatus() == 0) {
-//                        logger.info("通道离线,返回400");
-//                        responseAck(serverTransaction, Response.BAD_REQUEST, "channel [" + channel.getChannelId() + "] offline");
-//                        return;
-//                    }
                     // 通道存在,发100,TRYING
                     try {
-                        responseAck(serverTransaction, Response.TRYING);
+                        responseAck(request, Response.TRYING);
                     } catch (SipException | InvalidArgumentException | ParseException e) {
                         logger.error("[命令发送失败] invite TRYING: {}", e.getMessage());
                     }
@@ -226,7 +225,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                         if ("proxy".equals(gbStream.getStreamType())) {
                             logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
                             try {
-                                responseAck(serverTransaction, Response.GONE);
+                                responseAck(request, Response.GONE);
                             } catch (SipException | InvalidArgumentException | ParseException e) {
                                 logger.error("[命令发送失败] invite GONE: {}", e.getMessage());
                             }
@@ -236,7 +235,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                             if (streamPushItem == null || streamPushItem.getServerId().equals(userSetting.getServerId())) {
                                 logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
                                 try {
-                                    responseAck(serverTransaction, Response.GONE);
+                                    responseAck(request, Response.GONE);
                                 } catch (SipException | InvalidArgumentException | ParseException e) {
                                     logger.error("[命令发送失败] invite GONE: {}", e.getMessage());
                                 }
@@ -249,7 +248,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                             if (streamPushItem == null) {
                                 logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
                                 try {
-                                    responseAck(serverTransaction, Response.GONE);
+                                    responseAck(request, Response.GONE);
                                 } catch (SipException | InvalidArgumentException | ParseException e) {
                                     logger.error("[命令发送失败] invite GONE: {}", e.getMessage());
                                 }
@@ -260,7 +259,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                             if (proxyByAppAndStream == null) {
                                 logger.info("[ app={}, stream={} ]找不到zlm {},返回410", gbStream.getApp(), gbStream.getStream(), mediaServerId);
                                 try {
-                                    responseAck(serverTransaction, Response.GONE);
+                                    responseAck(request, Response.GONE);
                                 } catch (SipException | InvalidArgumentException | ParseException e) {
                                     logger.error("[命令发送失败] invite GONE: {}", e.getMessage());
                                 }
@@ -269,14 +268,14 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                         }
                     }
                     try {
-                        responseAck(serverTransaction, Response.CALL_IS_BEING_FORWARDED);
+                        responseAck(request, Response.CALL_IS_BEING_FORWARDED);
                     } catch (SipException | InvalidArgumentException | ParseException e) {
                         logger.error("[命令发送失败] invite CALL_IS_BEING_FORWARDED: {}", e.getMessage());
                     }
                 } else if (catalog != null) {
                     try {
                         // 目录不支持点播
-                        responseAck(serverTransaction, Response.BAD_REQUEST, "catalog channel can not play");
+                        responseAck(request, Response.BAD_REQUEST, "catalog channel can not play");
                     } catch (SipException | InvalidArgumentException | ParseException e) {
                         logger.error("[命令发送失败] invite 目录不支持点播: {}", e.getMessage());
                     }
@@ -285,7 +284,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     logger.info("通道不存在,返回404");
                     try {
                         // 通道不存在,发404,资源不存在
-                        responseAck(serverTransaction, Response.NOT_FOUND);
+                        responseAck(request, Response.NOT_FOUND);
                     } catch (SipException | InvalidArgumentException | ParseException e) {
                         logger.error("[命令发送失败] invite 通道不存在: {}", e.getMessage());
                     }
@@ -360,7 +359,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     // 回复不支持的格式
                     try {
                         // 不支持的格式,发415
-                        responseAck(serverTransaction, Response.UNSUPPORTED_MEDIA_TYPE);
+                        responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE);
                     } catch (SipException | InvalidArgumentException | ParseException e) {
                         logger.error("[命令发送失败] invite 不支持的格式: {}", e.getMessage());
                     }
@@ -377,7 +376,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     if (device == null) {
                         logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel);
                         try {
-                            responseAck(serverTransaction, Response.SERVER_INTERNAL_ERROR);
+                            responseAck(request, Response.SERVER_INTERNAL_ERROR);
                         } catch (SipException | InvalidArgumentException | ParseException e) {
                             logger.error("[命令发送失败] invite 未找到设备信息: {}", e.getMessage());
                         }
@@ -387,7 +386,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     if (mediaServerItem == null) {
                         logger.warn("未找到可用的zlm");
                         try {
-                            responseAck(serverTransaction, Response.BUSY_HERE);
+                            responseAck(request, Response.BUSY_HERE);
                         } catch (SipException | InvalidArgumentException | ParseException e) {
                             logger.error("[命令发送失败] invite BUSY_HERE: {}", e.getMessage());
                         }
@@ -403,7 +402,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     if (sendRtpItem == null) {
                         logger.warn("服务器端口资源不足");
                         try {
-                            responseAck(serverTransaction, Response.BUSY_HERE);
+                            responseAck(request, Response.BUSY_HERE);
                         } catch (SipException | InvalidArgumentException | ParseException e) {
                             logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage());
                         }
@@ -435,7 +434,12 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                         } else {
                             content.append("t=0 0\r\n");
                         }
-                        content.append("m=video " + sendRtpItem.getLocalPort() + " RTP/AVP 96\r\n");
+                        int localPort = sendRtpItem.getLocalPort();
+                        if (localPort == 0) {
+                            // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口
+                            localPort = new Random().nextInt(65535) + 1;
+                        }
+                        content.append("m=video " + localPort + " RTP/AVP 96\r\n");
                         content.append("a=sendonly\r\n");
                         content.append("a=rtpmap:96 PS/90000\r\n");
                         content.append("y=" + sendRtpItem.getSsrc() + "\r\n");
@@ -453,7 +457,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                                     logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
                                 }
                             }, 60 * 1000);
-                            responseSdpAck(serverTransaction, content.toString(), platform);
+                            responseSdpAck(request, content.toString(), platform);
 
                         } catch (SipException e) {
                             e.printStackTrace();
@@ -467,21 +471,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                         // 未知错误。直接转发设备点播的错误
                         try {
                             Response response = getMessageFactory().createResponse(event.statusCode, evt.getRequest());
-                            serverTransaction.sendResponse(response);
-                            System.out.println("未知错误。直接转发设备点播的错误");
-                            if (serverTransaction.getDialog() != null) {
-                                serverTransaction.getDialog().delete();
-                            }
-                            serverTransaction.getDialog().delete();
-
-                        } catch (ParseException | SipException | InvalidArgumentException e) {
+                            sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
+                        } catch (ParseException | SipException  e) {
                             e.printStackTrace();
                         }
                     });
                     sendRtpItem.setApp("rtp");
                     if ("Playback".equalsIgnoreCase(sessionName)) {
                         sendRtpItem.setPlayType(InviteStreamType.PLAYBACK);
-                        SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, true, true);
+                        SSRCInfo ssrcInfo = mediaServerService.openRTPServer(mediaServerItem, null, device.isSsrcCheck(), true);
                         sendRtpItem.setStreamId(ssrcInfo.getStream());
                         // 写入redis, 超时时回复
                         redisCatchStorage.updateSendRTPSever(sendRtpItem);
@@ -494,13 +492,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                                         }
                                         redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), finalChannelId, callIdHeader.getCallId(), null);
                                         try {
-                                            responseAck(serverTransaction, Response.REQUEST_TIMEOUT);
-                                        } catch (SipException e) {
-                                            e.printStackTrace();
-                                        } catch (InvalidArgumentException e) {
-                                            e.printStackTrace();
-                                        } catch (ParseException e) {
-                                            e.printStackTrace();
+                                            responseAck(request, Response.REQUEST_TIMEOUT);
+                                        } catch (SipException | InvalidArgumentException | ParseException e) {
+                                            logger.error("[命令发送失败] 国标级联 录像回放 发送REQUEST_TIMEOUT: {}", e.getMessage());
                                         }
                                     } else {
                                         if (result.getMediaServerItem() != null) {
@@ -536,10 +530,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
 
                             // 写入redis, 超时时回复
                             redisCatchStorage.updateSendRTPSever(sendRtpItem);
+                            MediaServerItem finalMediaServerItem = mediaServerItem;
                             playService.play(mediaServerItem, ssrcInfo, device, channelId, hookEvent, errorEvent, (code, msg) -> {
-                                logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, finalChannelId);
-                                redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), finalChannelId, callIdHeader.getCallId(), null);
-                            }, null);
+                                logger.info("[上级点播]超时, 用户:{}, 通道:{}", username, channelId);
+                                redisCatchStorage.deleteSendRTPServer(platform.getServerGBId(), channelId, callIdHeader.getCallId(), null);
+                            });
                         } else {
                             sendRtpItem.setStreamId(playTransaction.getStream());
                             // 写入redis, 超时时回复
@@ -554,30 +549,26 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     if("push".equals(gbStream.getStreamType())) {
                         if (streamPushItem != null && streamPushItem.isPushIng()) {
                             // 推流状态
-                            pushStream(evt, serverTransaction, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                            pushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
                                     mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
                         } else {
                             // 未推流 拉起
-                            notifyStreamOnline(evt, serverTransaction,gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                            notifyStreamOnline(evt, request,gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
                                     mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
                         }
                     }else if ("proxy".equals(gbStream.getStreamType())){
-                        if(null != proxyByAppAndStream &&proxyByAppAndStream.isStatus()){
-                            pushProxyStream(evt, serverTransaction, gbStream,  platform, callIdHeader, mediaServerItem, port, tcpActive,
-                                    mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
-                        }else{
-                            //开启代理拉流
-                            boolean start1 = streamProxyService.start(gbStream.getApp(), gbStream.getStream());
-                            if(start1) {
-                                pushProxyStream(evt, serverTransaction, gbStream,  platform, callIdHeader, mediaServerItem, port, tcpActive,
+                        if (null != proxyByAppAndStream) {
+                            if(proxyByAppAndStream.isStatus()){
+                                pushProxyStream(evt, request, gbStream,  platform, callIdHeader, mediaServerItem, port, tcpActive,
                                         mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
                             }else{
-                                //失败后通知
-                                notifyStreamOnline(evt, serverTransaction,gbStream, null, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                                //开启代理拉流
+                                notifyStreamOnline(evt, request,gbStream, null, platform, callIdHeader, mediaServerItem, port, tcpActive,
                                         mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
                             }
                         }
 
+
                     }
                 }
             }
@@ -591,7 +582,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
     /**
      * 安排推流
      */
-    private void pushProxyStream(RequestEvent evt, ServerTransaction serverTransaction, GbStream gbStream, ParentPlatform platform,
+    private void pushProxyStream(RequestEvent evt, SIPRequest request, GbStream gbStream, ParentPlatform platform,
                             CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
                             int port, Boolean tcpActive, boolean mediaTransmissionTCP,
                             String channelId, String addressStr, String ssrc, String requesterId) {
@@ -605,7 +596,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                 if (sendRtpItem == null) {
                     logger.warn("服务器端口资源不足");
                     try {
-                        responseAck(serverTransaction, Response.BUSY_HERE);
+                        responseAck(request, Response.BUSY_HERE);
                     } catch (SipException | InvalidArgumentException | ParseException e) {
                         logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage());
                     }
@@ -618,10 +609,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                 // 写入redis, 超时时回复
                 sendRtpItem.setStatus(1);
                 sendRtpItem.setCallId(callIdHeader.getCallId());
-                SIPRequest request = (SIPRequest) evt.getRequest();
                 sendRtpItem.setFromTag(request.getFromTag());
 
-                SIPResponse response = sendStreamAck(mediaServerItem, serverTransaction, sendRtpItem, platform, evt);
+                SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt);
                 if (response != null) {
                     sendRtpItem.setToTag(response.getToTag());
                 }
@@ -630,7 +620,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
         }
 
     }
-    private void pushStream(RequestEvent evt, ServerTransaction serverTransaction, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
+    private void pushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
                             CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
                             int port, Boolean tcpActive, boolean mediaTransmissionTCP,
                             String channelId, String addressStr, String ssrc, String requesterId) {
@@ -646,7 +636,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                 if (sendRtpItem == null) {
                     logger.warn("服务器端口资源不足");
                     try {
-                        responseAck(serverTransaction, Response.BUSY_HERE);
+                        responseAck(request, Response.BUSY_HERE);
                     } catch (SipException | InvalidArgumentException | ParseException e) {
                         logger.error("[命令发送失败] invite 服务器端口资源不足: {}", e.getMessage());
                     }
@@ -660,9 +650,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                 sendRtpItem.setStatus(1);
                 sendRtpItem.setCallId(callIdHeader.getCallId());
 
-                SIPRequest request = (SIPRequest) evt.getRequest();
                 sendRtpItem.setFromTag(request.getFromTag());
-                SIPResponse response = sendStreamAck(mediaServerItem, serverTransaction, sendRtpItem, platform, evt);
+                SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt);
                 if (response != null) {
                     sendRtpItem.setToTag(response.getToTag());
                 }
@@ -671,36 +660,59 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
 
             } else {
                 // 不在线 拉起
-                notifyStreamOnline(evt, serverTransaction,gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                notifyStreamOnline(evt, request,gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
                         mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
             }
 
         } else {
             // 其他平台内容
-            otherWvpPushStream(evt, serverTransaction, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
+            otherWvpPushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
                     mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
         }
     }
     /**
      * 通知流上线
      */
-    private void notifyStreamOnline(RequestEvent evt, ServerTransaction serverTransaction, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
+    private void notifyStreamOnline(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
                                     CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
                                     int port, Boolean tcpActive, boolean mediaTransmissionTCP,
                                     String channelId, String addressStr, String ssrc, String requesterId) {
         if ("proxy".equals(gbStream.getStreamType())) {
             // TODO 控制启用以使设备上线
             logger.info("[ app={}, stream={} ]通道未推流,启用流后开始推流", gbStream.getApp(), gbStream.getStream());
-            try {
-                responseAck(serverTransaction, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");
-            } catch (SipException | InvalidArgumentException | ParseException e) {
-                logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage());
+            // 监听流上线
+            HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed(gbStream.getApp(), gbStream.getStream(), true, "rtsp", mediaServerItem.getId());
+            zlmHttpHookSubscribe.addSubscribe(hookSubscribe, (mediaServerItemInUSe, responseJSON) -> {
+                String app = responseJSON.getString("app");
+                String stream = responseJSON.getString("stream");
+                logger.info("[上级点播]拉流代理已经就绪, {}/{}", app, stream);
+                dynamicTask.stop(callIdHeader.getCallId());
+                pushProxyStream(evt, request, gbStream,  platform, callIdHeader, mediaServerItem, port, tcpActive,
+                        mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
+            });
+            dynamicTask.startDelay(callIdHeader.getCallId(), () -> {
+                logger.info("[ app={}, stream={} ] 等待拉流代理流超时", gbStream.getApp(), gbStream.getStream());
+                zlmHttpHookSubscribe.removeSubscribe(hookSubscribe);
+            }, userSetting.getPlatformPlayTimeout());
+            boolean start = streamProxyService.start(gbStream.getApp(), gbStream.getStream());
+            if (!start) {
+                try {
+                    responseAck(request, Response.BUSY_HERE, "channel [" + gbStream.getGbId() + "] offline");
+                } catch (SipException | InvalidArgumentException | ParseException e) {
+                    logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage());
+                }
+                zlmHttpHookSubscribe.removeSubscribe(hookSubscribe);
+                dynamicTask.stop(callIdHeader.getCallId());
             }
+
+
+
         } else if ("push".equals(gbStream.getStreamType())) {
             if (!platform.isStartOfflinePush()) {
                 // 平台设置中关闭了拉起离线的推流则直接回复
                 try {
-                    responseAck(serverTransaction, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing");
+                    logger.info("[上级点播] 失败,推流设备未推流,channel: {}, app: {}, stream: {}", gbStream.getGbId(), gbStream.getApp(), gbStream.getStream());
+                    responseAck(request, Response.TEMPORARILY_UNAVAILABLE, "channel stream not pushing");
                 } catch (SipException | InvalidArgumentException | ParseException e) {
                     logger.error("[命令发送失败] invite 通道未推流: {}", e.getMessage());
                 }
@@ -718,7 +730,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                 logger.info("[ app={}, stream={} ] 等待设备开始推流超时", gbStream.getApp(), gbStream.getStream());
                 try {
                     mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream());
-                    responseAck(serverTransaction, Response.REQUEST_TIMEOUT); // 超时
+                    responseAck(request, Response.REQUEST_TIMEOUT); // 超时
                 } catch (SipException e) {
                     e.printStackTrace();
                 } catch (InvalidArgumentException e) {
@@ -741,7 +753,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     if (sendRtpItem == null) {
                         logger.warn("上级点时创建sendRTPItem失败,可能是服务器端口资源不足");
                         try {
-                            responseAck(serverTransaction, Response.BUSY_HERE);
+                            responseAck(request, Response.BUSY_HERE);
                         } catch (SipException e) {
                             e.printStackTrace();
                         } catch (InvalidArgumentException e) {
@@ -759,16 +771,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     sendRtpItem.setStatus(1);
                     sendRtpItem.setCallId(callIdHeader.getCallId());
 
-                    SIPRequest request = (SIPRequest) evt.getRequest();
                     sendRtpItem.setFromTag(request.getFromTag());
-                    SIPResponse response = sendStreamAck(mediaServerItem, serverTransaction, sendRtpItem, platform, evt);
+                    SIPResponse response = sendStreamAck(mediaServerItem, request, sendRtpItem, platform, evt);
                     if (response != null) {
                         sendRtpItem.setToTag(response.getToTag());
                     }
                     redisCatchStorage.updateSendRTPSever(sendRtpItem);
                 } else {
                     // 其他平台内容
-                    otherWvpPushStream(evt, serverTransaction, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                    otherWvpPushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
                             mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
                 }
             });
@@ -779,7 +790,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     dynamicTask.stop(callIdHeader.getCallId());
                     mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream());
                     try {
-                        responseAck(serverTransaction, Response.TEMPORARILY_UNAVAILABLE, response.getMsg());
+                        responseAck(request, Response.TEMPORARILY_UNAVAILABLE, response.getMsg());
                     } catch (SipException | InvalidArgumentException | ParseException e) {
                         logger.error("[命令发送失败] 国标级联 点播回复: {}", e.getMessage());
                     }
@@ -791,7 +802,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
     /**
      * 来自其他wvp的推流
      */
-    private void otherWvpPushStream(RequestEvent evt, ServerTransaction serverTransaction, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
+    private void otherWvpPushStream(RequestEvent evt, SIPRequest request, GbStream gbStream, StreamPushItem streamPushItem, ParentPlatform platform,
                                     CallIdHeader callIdHeader, MediaServerItem mediaServerItem,
                                     int port, Boolean tcpActive, boolean mediaTransmissionTCP,
                                     String channelId, String addressStr, String ssrc, String requesterId) {
@@ -804,7 +815,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     if (sendRtpItem == null || responseSendItemMsg.getMediaServerItem() == null) {
                         logger.warn("服务器端口资源不足");
                         try {
-                            responseAck(serverTransaction, Response.BUSY_HERE);
+                            responseAck(request, Response.BUSY_HERE);
                         } catch (SipException e) {
                             e.printStackTrace();
                         } catch (InvalidArgumentException e) {
@@ -823,9 +834,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     sendRtpItem.setStatus(1);
                     sendRtpItem.setCallId(callIdHeader.getCallId());
 
-                    SIPRequest request = (SIPRequest) evt.getRequest();
                     sendRtpItem.setFromTag(request.getFromTag());
-                    SIPResponse response = sendStreamAck(responseSendItemMsg.getMediaServerItem(), serverTransaction,sendRtpItem, platform, evt);
+                    SIPResponse response = sendStreamAck(responseSendItemMsg.getMediaServerItem(), request,sendRtpItem, platform, evt);
                     if (response != null) {
                         sendRtpItem.setToTag(response.getToTag());
                     }
@@ -839,24 +849,24 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                         StreamPushItem currentStreamPushItem = streamPushService.getPush(streamPushItem.getApp(), streamPushItem.getStream());
                         if (currentStreamPushItem.isPushIng()) {
                             // 在线状态
-                            pushStream(evt, serverTransaction, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                            pushStream(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
                                     mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
 
                         } else {
                             // 不在线 拉起
-                            notifyStreamOnline(evt, serverTransaction, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
+                            notifyStreamOnline(evt, request, gbStream, streamPushItem, platform, callIdHeader, mediaServerItem, port, tcpActive,
                                     mediaTransmissionTCP, channelId, addressStr, ssrc, requesterId);
                         }
                     }
                     try {
-                        responseAck(serverTransaction, Response.BUSY_HERE);
+                        responseAck(request, Response.BUSY_HERE);
                     } catch (InvalidArgumentException | ParseException | SipException e) {
                         logger.error("[命令发送失败] 国标级联 点播回复 BUSY_HERE: {}", e.getMessage());
                     }
                 });
     }
 
-    public SIPResponse sendStreamAck(MediaServerItem mediaServerItem, ServerTransaction serverTransaction, SendRtpItem sendRtpItem, ParentPlatform platform, RequestEvent evt) {
+    public SIPResponse sendStreamAck(MediaServerItem mediaServerItem, SIPRequest request, SendRtpItem sendRtpItem, ParentPlatform platform, RequestEvent evt) {
 
         StringBuffer content = new StringBuffer(200);
         content.append("v=0\r\n");
@@ -879,7 +889,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
         content.append("f=\r\n");
 
         try {
-            return responseSdpAck(serverTransaction, content.toString(), platform);
+            return responseSdpAck(request, content.toString(), platform);
         } catch (SipException e) {
             e.printStackTrace();
         } catch (InvalidArgumentException e) {
@@ -890,7 +900,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
         return null;
     }
 
-    public void inviteFromDeviceHandle(ServerTransaction serverTransaction, String requesterId, String channelId) {
+    public void inviteFromDeviceHandle(SIPRequest request, String requesterId, String channelId) {
+
         // 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备)
         Device device = redisCatchStorage.getDevice(requesterId);
         AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(requesterId, channelId);
@@ -907,11 +918,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
         if (device != null) {
             logger.info("收到设备" + requesterId + "的语音广播Invite请求");
             try {
-                responseAck(serverTransaction, Response.TRYING);
+                responseAck(request, Response.TRYING);
             } catch (SipException | InvalidArgumentException | ParseException e) {
                 logger.error("[命令发送失败] invite BAD_REQUEST: {}", e.getMessage());
             }
-            String contentString = new String(serverTransaction.getRequest().getRawContent());
+            String contentString = new String(request.getRawContent());
             // jainSip不支持y=字段, 移除移除以解析。
             String substring = contentString;
             String ssrc = "0000000404";
@@ -961,7 +972,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
                     logger.info("不支持的媒体格式,返回415");
                     // 回复不支持的格式
                     try {
-                        responseAck(serverTransaction, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
+                        responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
                     } catch (SipException | InvalidArgumentException | ParseException e) {
                         logger.error("[命令发送失败] invite 不支持的媒体格式: {}", e.getMessage());
                     }
@@ -1024,7 +1035,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
         } else {
             logger.warn("来自无效设备/平台的请求");
             try {
-                responseAck(serverTransaction, Response.BAD_REQUEST);; // 不支持的格式,发415
+                responseAck(request, Response.BAD_REQUEST);; // 不支持的格式,发415
             } catch (SipException | InvalidArgumentException | ParseException e) {
                 logger.error("[命令发送失败] invite 来自无效设备/平台的请求, {}", e.getMessage());
             }

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

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.*;
@@ -19,6 +19,7 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.DocumentException;
 import org.dom4j.Element;
 import org.slf4j.Logger;
@@ -33,7 +34,6 @@ import org.springframework.util.StringUtils;
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.RequestEvent;
-import javax.sip.ServerTransaction;
 import javax.sip.SipException;
 import javax.sip.header.FromHeader;
 import javax.sip.message.Response;
@@ -92,9 +92,8 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
 
 	@Override
 	public void process(RequestEvent evt) {
-		ServerTransaction serverTransaction = getServerTransaction(evt);
 		try {
-			responseAck(serverTransaction, Response.OK);
+			responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null);
 		}catch (SipException | InvalidArgumentException | ParseException e) {
 			e.printStackTrace();
 		}

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

@@ -1,19 +1,20 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
 
 import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
-import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
 import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import gov.nist.javax.sip.RequestEventExt;
 import gov.nist.javax.sip.address.AddressImpl;
 import gov.nist.javax.sip.address.SipUri;
-import gov.nist.javax.sip.header.Expires;
 import gov.nist.javax.sip.header.SIPDateHeader;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
@@ -24,6 +25,12 @@ import org.springframework.util.ObjectUtils;
 import javax.sip.*;
 import javax.sip.header.*;
 import javax.sip.message.Request;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
+import javax.sip.header.AuthorizationHeader;
+import javax.sip.header.ContactHeader;
+import javax.sip.header.FromHeader;
+import javax.sip.header.ViaHeader;
 import javax.sip.message.Response;
 import java.security.NoSuchAlgorithmException;
 import java.text.ParseException;
@@ -49,6 +56,9 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
     @Autowired
     private IDeviceService deviceService;
 
+    @Autowired
+    private SIPSender sipSender;
+
     @Override
     public void afterPropertiesSet() throws Exception {
         // 添加消息处理的订阅
@@ -66,41 +76,39 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
             RequestEventExt evtExt = (RequestEventExt) evt;
             String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort();
             logger.info("[注册请求] 开始处理: {}", requestAddress);
-            Request request = evt.getRequest();
-            ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME);
+            SIPRequest request = (SIPRequest)evt.getRequest();
             Response response = null;
             boolean passwordCorrect = false;
             // 注册标志
-            boolean registerFlag = false;
+            boolean registerFlag;
             FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
             AddressImpl address = (AddressImpl) fromHeader.getAddress();
             SipUri uri = (SipUri) address.getURI();
             String deviceId = uri.getUser();
-
+            Device device = deviceService.getDevice(deviceId);
+            String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword();
             AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
-            if (authHead == null && !ObjectUtils.isEmpty(sipConfig.getPassword())) {
-                logger.info("[注册请求] 未携带授权头 回复401: {}", requestAddress);
+            if (authHead == null && !ObjectUtils.isEmpty(password)) {
+                logger.info("[注册请求] 回复401: {}", requestAddress);
                 response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request);
                 new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain());
-                sendResponse(evt, response);
+                sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
                 return;
             }
 
             // 校验密码是否正确
-            passwordCorrect = ObjectUtils.isEmpty(sipConfig.getPassword()) ||
-                    new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, sipConfig.getPassword());
+            passwordCorrect = ObjectUtils.isEmpty(password) ||
+                    new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request, password);
 
             if (!passwordCorrect) {
                 // 注册失败
                 response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
                 response.setReasonPhrase("wrong password");
                 logger.info("[注册请求] 密码/SIP服务器ID错误, 回复403: {}", requestAddress);
-                sendResponse(evt, response);
+                sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
                 return;
             }
 
-            Device device = deviceService.queryDevice(deviceId);
-
             // 携带授权头并且密码正确
             response = getMessageFactory().createResponse(Response.OK, request);
             // 添加date头
@@ -110,7 +118,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
             dateHeader.setDate(wvpSipDate);
             response.addHeader(dateHeader);
 
-            if (expiresHeader == null) {
+            if (request.getExpires() == null) {
                 response = getMessageFactory().createResponse(Response.BAD_REQUEST, request);
                 if (evt.getDialog() != null ) {
                     if (evt.getDialog().isServer()) {
@@ -119,6 +127,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
                         serverTransaction.getDialog().delete();
                     }
                 }
+                sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
                 return;
             }
             // 添加Contact头
@@ -147,12 +156,13 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
             device.setIp(received);
             device.setPort(rPort);
             device.setHostAddress(received.concat(":").concat(String.valueOf(rPort)));
-            if (expiresHeader.getExpires() == 0) {
+            device.setLocalIp(request.getLocalAddress().getHostAddress());
+            if (request.getExpires().getExpires() == 0) {
                 // 注销成功
                 registerFlag = false;
             } else {
                 // 注册成功
-                device.setExpires(expiresHeader.getExpires());
+                device.setExpires(request.getExpires().getExpires());
                 registerFlag = true;
                 // 判断TCP还是UDP
                 ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
@@ -160,7 +170,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
                 device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
             }
 
-            sendResponse(evt, response);
+            sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
             // 注册成功
             // 保存到redis
             if (registerFlag) {
@@ -171,7 +181,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
                 logger.info("[注销成功] deviceId: {}->{}" ,deviceId, requestAddress);
                 deviceService.offline(deviceId);
             }
-        } catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) {
+        } catch (SipException | NoSuchAlgorithmException | ParseException e) {
             e.printStackTrace();
         }
     }

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

@@ -7,6 +7,9 @@ import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
 import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
@@ -15,23 +18,16 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import gov.nist.javax.sip.SipProviderImpl;
 import gov.nist.javax.sip.message.SIPRequest;
 import gov.nist.javax.sip.message.SIPResponse;
-import gov.nist.javax.sip.stack.SIPClientTransaction;
-import gov.nist.javax.sip.stack.SIPDialog;
-import gov.nist.javax.sip.stack.SIPServerTransaction;
-import gov.nist.javax.sip.stack.SIPServerTransactionImpl;
 import org.dom4j.DocumentException;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
 
 import javax.sip.*;
 import javax.sip.header.ExpiresHeader;
-import javax.sip.message.Request;
 import javax.sip.message.Response;
 import java.text.ParseException;
 
@@ -54,6 +50,9 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
 	@Autowired
 	private SubscribeHolder subscribeHolder;
 
+	@Autowired
+	private SIPSender sipSender;
+
 	@Override
 	public void afterPropertiesSet() throws Exception {
 		// 添加消息处理的订阅
@@ -67,8 +66,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
 	 */
 	@Override
 	public void process(RequestEvent evt) {
-		ServerTransaction serverTransaction = getServerTransaction(evt);
-		Request request = evt.getRequest();
+		SIPRequest request = (SIPRequest) evt.getRequest();
 		try {
 			Element rootElement = getRootElement(evt);
 			if (rootElement == null) {
@@ -77,12 +75,12 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
 			}
 			String cmd = XmlUtil.getText(rootElement, "CmdType");
 			if (CmdType.MOBILE_POSITION.equals(cmd)) {
-				processNotifyMobilePosition(serverTransaction, rootElement);
+				processNotifyMobilePosition(request, rootElement);
 //			} else if (CmdType.ALARM.equals(cmd)) {
 //				logger.info("接收到Alarm订阅");
 //				processNotifyAlarm(serverTransaction, rootElement);
 			} else if (CmdType.CATALOG.equals(cmd)) {
-				processNotifyCatalogList(serverTransaction, rootElement);
+				processNotifyCatalogList(request, rootElement);
 			} else {
 				logger.info("接收到消息:" + cmd);
 
@@ -92,13 +90,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
 					response.setExpires(expireHeader);
 				}
 				logger.info("response : " + response);
-				ServerTransaction transaction = getServerTransaction(evt);
-				if (transaction != null) {
-					transaction.sendResponse(response);
-					transaction.terminate();
-				} else {
-					logger.info("processRequest serverTransactionId is null.");
-				}
+				sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
 			}
 		} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
 			e.printStackTrace();
@@ -109,14 +101,14 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
 	/**
 	 * 处理移动位置订阅消息
 	 */
-	private void processNotifyMobilePosition(ServerTransaction serverTransaction, Element rootElement) throws SipException {
-		if (serverTransaction == null) {
+	private void processNotifyMobilePosition(SIPRequest request, Element rootElement) throws SipException {
+		if (request == null) {
 			return;
 		}
-		String platformId = SipUtils.getUserIdFromFromHeader(serverTransaction.getRequest());
+		String platformId = SipUtils.getUserIdFromFromHeader(request);
 		String deviceId = XmlUtil.getText(rootElement, "DeviceID");
 		ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
-		SubscribeInfo subscribeInfo = new SubscribeInfo(serverTransaction, platformId);
+		SubscribeInfo subscribeInfo = new SubscribeInfo(request, platformId);
 		if (platform == null) {
 			return;
 		}
@@ -145,7 +137,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
 
 		try {
 			ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformId);
-			SIPResponse response = responseXmlAck(serverTransaction, resultXml.toString(), parentPlatform, subscribeInfo.getExpires());
+			SIPResponse response = responseXmlAck(request, resultXml.toString(), parentPlatform, subscribeInfo.getExpires());
 			if (subscribeInfo.getExpires() == 0) {
 				subscribeHolder.removeMobilePositionSubscribe(platformId);
 			}else {
@@ -162,17 +154,17 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
 
 	}
 
-	private void processNotifyCatalogList(ServerTransaction serverTransaction, Element rootElement) throws SipException {
-		if (serverTransaction == null) {
+	private void processNotifyCatalogList(SIPRequest request, Element rootElement) throws SipException {
+		if (request == null) {
 			return;
 		}
-		String platformId = SipUtils.getUserIdFromFromHeader(serverTransaction.getRequest());
+		String platformId = SipUtils.getUserIdFromFromHeader(request);
 		String deviceId = XmlUtil.getText(rootElement, "DeviceID");
 		ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
 		if (platform == null){
 			return;
 		}
-		SubscribeInfo subscribeInfo = new SubscribeInfo(serverTransaction, platformId);
+		SubscribeInfo subscribeInfo = new SubscribeInfo(request, platformId);
 
 		String sn = XmlUtil.getText(rootElement, "SN");
 		logger.info("[回复上级的目录订阅请求]: {}/{}", platformId, deviceId);
@@ -192,7 +184,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
 		}
 		try {
 			ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(platformId);
-			SIPResponse response = responseXmlAck(serverTransaction, resultXml.toString(), parentPlatform, subscribeInfo.getExpires());
+			SIPResponse response = responseXmlAck(request, resultXml.toString(), parentPlatform, subscribeInfo.getExpires());
 			if (subscribeInfo.getExpires() == 0) {
 				subscribeHolder.removeCatalogSubscribe(platformId);
 			}else {

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

@@ -19,7 +19,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import javax.sip.InvalidArgumentException;
 import javax.sip.RequestEvent;
-import javax.sip.ServerTransaction;
 import javax.sip.SipException;
 import javax.sip.header.*;
 import javax.sip.message.Response;
@@ -62,8 +61,9 @@ public class InfoRequestProcessor extends SIPRequestProcessorParent implements I
     @Override
     public void process(RequestEvent evt) {
         logger.debug("接收到消息:" + evt.getRequest());
-        String deviceId = SipUtils.getUserIdFromFromHeader(evt.getRequest());
-        CallIdHeader callIdHeader = (CallIdHeader)evt.getRequest().getHeader(CallIdHeader.NAME);
+        SIPRequest request = (SIPRequest) evt.getRequest();
+        String deviceId = SipUtils.getUserIdFromFromHeader(request);
+        CallIdHeader callIdHeader = request.getCallIdHeader();
         // 先从会话内查找
         SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransaction(null, null, callIdHeader.getCallId(), null);
 
@@ -71,7 +71,6 @@ public class InfoRequestProcessor extends SIPRequestProcessorParent implements I
         if (ssrcTransaction != null) {
             deviceId = ssrcTransaction.getDeviceId();
         }
-        ServerTransaction serverTransaction = getServerTransaction(evt);
         // 查询设备是否存在
         Device device = redisCatchStorage.getDevice(deviceId);
         // 查询上级平台是否存在
@@ -79,7 +78,6 @@ public class InfoRequestProcessor extends SIPRequestProcessorParent implements I
         try {
             if (device != null && parentPlatform != null) {
                 logger.warn("[重复]平台与设备编号重复:{}", deviceId);
-                SIPRequest request = (SIPRequest) evt.getRequest();
                 String hostAddress = request.getRemoteAddress().getHostAddress();
                 int remotePort = request.getRemotePort();
                 if (device.getHostAddress().equals(hostAddress + ":" + remotePort)) {
@@ -90,7 +88,7 @@ public class InfoRequestProcessor extends SIPRequestProcessorParent implements I
             }
             if (device == null && parentPlatform == null) {
                 // 不存在则回复404
-                responseAck(serverTransaction, Response.NOT_FOUND, "device "+ deviceId +" not found");
+                responseAck(request, Response.NOT_FOUND, "device "+ deviceId +" not found");
                 logger.warn("[设备未找到 ]: {}", deviceId);
                 if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null){
                     DeviceNotFoundEvent deviceNotFoundEvent = new DeviceNotFoundEvent(evt.getDialog());
@@ -107,21 +105,21 @@ public class InfoRequestProcessor extends SIPRequestProcessorParent implements I
                     String streamId = sendRtpItem.getStreamId();
                     StreamInfo streamInfo = redisCatchStorage.queryPlayback(null, null, streamId, null);
                     if (null == streamInfo) {
-                        responseAck(serverTransaction, Response.NOT_FOUND, "stream " + streamId + " not found");
+                        responseAck(request, Response.NOT_FOUND, "stream " + streamId + " not found");
                         return;
                     }
                     Device device1 = storager.queryVideoDevice(streamInfo.getDeviceID());
                     cmder.playbackControlCmd(device1,streamInfo,new String(evt.getRequest().getRawContent()),eventResult -> {
                         // 失败的回复
                         try {
-                            responseAck(serverTransaction, eventResult.statusCode, eventResult.msg);
+                            responseAck(request, eventResult.statusCode, eventResult.msg);
                         } catch (SipException | InvalidArgumentException | ParseException e) {
                             logger.error("[命令发送失败] 国标级联 录像控制: {}", e.getMessage());
                         }
                     }, eventResult -> {
                         // 成功的回复
                         try {
-                            responseAck(serverTransaction, eventResult.statusCode);
+                            responseAck(request, eventResult.statusCode);
                         } catch (SipException | InvalidArgumentException | ParseException e) {
                             logger.error("[命令发送失败] 国标级联 录像控制: {}", e.getMessage());
                         }

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

@@ -23,12 +23,8 @@ import org.springframework.stereotype.Component;
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.RequestEvent;
-import javax.sip.ServerTransaction;
 import javax.sip.SipException;
-import javax.sip.address.SipURI;
-import javax.sip.header.CSeqHeader;
 import javax.sip.header.CallIdHeader;
-import javax.sip.message.Request;
 import javax.sip.message.Response;
 import java.text.ParseException;
 import java.util.Map;
@@ -80,16 +76,13 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
         if (ssrcTransaction != null) {
             deviceId = ssrcTransaction.getDeviceId();
         }
-
-        ServerTransaction serverTransaction = getServerTransaction(evt);
-
+        SIPRequest request = (SIPRequest) evt.getRequest();
         // 查询设备是否存在
         Device device = redisCatchStorage.getDevice(deviceId);
         // 查询上级平台是否存在
         ParentPlatform parentPlatform = storage.queryParentPlatByServerGBId(deviceId);
         try {
             if (device != null && parentPlatform != null) {
-                SIPRequest request = (SIPRequest) evt.getRequest();
                 String hostAddress = request.getRemoteAddress().getHostAddress();
                 int remotePort = request.getRemotePort();
                 if (device.getHostAddress().equals(hostAddress + ":" + remotePort)) {
@@ -100,7 +93,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
             }
             if (device == null && parentPlatform == null) {
                 // 不存在则回复404
-                responseAck(serverTransaction, Response.NOT_FOUND, "device "+ deviceId +" not found");
+                responseAck(request, Response.NOT_FOUND, "device "+ deviceId +" not found");
                 logger.warn("[设备未找到 ]: {}", deviceId);
                 if (sipSubscribe.getErrorSubscribe(callIdHeader.getCallId()) != null){
                     DeviceNotFoundEvent deviceNotFoundEvent = new DeviceNotFoundEvent(evt.getDialog());
@@ -114,13 +107,13 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
                     rootElement = getRootElement(evt);
                     if (rootElement == null) {
                         logger.error("处理MESSAGE请求  未获取到消息体{}", evt.getRequest());
-                        responseAck(serverTransaction, Response.BAD_REQUEST, "content is null");
+                        responseAck(request, Response.BAD_REQUEST, "content is null");
                         return;
                     }
                 } catch (DocumentException e) {
                     logger.warn("解析XML消息内容异常", e);
                     // 不存在则回复404
-                    responseAck(serverTransaction, Response.BAD_REQUEST, e.getMessage());
+                    responseAck(request, Response.BAD_REQUEST, e.getMessage());
                 }
                 String name = rootElement.getName();
                 IMessageHandler messageHandler = messageHandlerMap.get(name);
@@ -133,7 +126,7 @@ public class MessageRequestProcessor extends SIPRequestProcessorParent implement
                 }else {
                     // 不支持的message
                     // 不存在则回复415
-                    responseAck(serverTransaction, Response.UNSUPPORTED_MEDIA_TYPE, "Unsupported message type, must Control/Notify/Query/Response");
+                    responseAck(request, Response.UNSUPPORTED_MEDIA_TYPE, "Unsupported message type, must Control/Notify/Query/Response");
                 }
             }
         } catch (SipException e) {

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

@@ -9,8 +9,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.control.ControlMessageHandler;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
-import com.genersoft.iot.vmp.utils.SpringBeanFactory;
-import gov.nist.javax.sip.SipStackImpl;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -67,10 +66,10 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
     @Override
     public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
 
-        ServerTransaction serverTransaction = getServerTransaction(evt);
+        SIPRequest request = (SIPRequest) evt.getRequest();
 
         // 此处是上级发出的DeviceControl指令
-        String targetGBId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
+        String targetGBId = ((SipURI) request.getToHeader().getAddress().getURI()).getUser();
         String channelId = getText(rootElement, "DeviceID");
         // 远程启动功能
         if (!ObjectUtils.isEmpty(getText(rootElement, "TeleBoot"))) {
@@ -83,23 +82,24 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
                     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());
-                    }
+                    // 远程启动
+//                    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());
+//                    }
                 });
             } else {
                 // 远程启动指定设备
@@ -111,7 +111,7 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
             Device deviceForPlatform = storager.queryVideoDeviceByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
             if (deviceForPlatform == null) {
                 try {
-                    responseAck(serverTransaction, Response.NOT_FOUND);
+                    responseAck(request, Response.NOT_FOUND);
                 } catch (SipException | InvalidArgumentException | ParseException e) {
                     logger.error("[命令发送失败] 错误信息: {}", e.getMessage());
                 }
@@ -121,14 +121,14 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
                 cmder.fronEndCmd(deviceForPlatform, channelId, cmdString, eventResult -> {
                     // 失败的回复
                     try {
-                        responseAck(serverTransaction, eventResult.statusCode, eventResult.msg);
+                        responseAck(request, eventResult.statusCode, eventResult.msg);
                     } catch (SipException | InvalidArgumentException | ParseException e) {
                         logger.error("[命令发送失败] 云台/前端回复: {}", e.getMessage());
                     }
                 }, eventResult -> {
                     // 成功的回复
                     try {
-                        responseAck(serverTransaction, eventResult.statusCode);
+                        responseAck(request, eventResult.statusCode);
                     } catch (SipException | InvalidArgumentException | ParseException e) {
                         logger.error("[命令发送失败] 云台/前端回复: {}", e.getMessage());
                     }

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

@@ -1,6 +1,7 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.conf.SipConfig;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.*;
@@ -16,6 +17,7 @@ import com.genersoft.iot.vmp.service.IDeviceChannelService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.utils.DateUtil;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -97,7 +99,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
                     SipMsgInfo sipMsgInfo = taskQueue.poll();
                     // 回复200 OK
                     try {
-                        responseAck(getServerTransaction(sipMsgInfo.getEvt()), Response.OK);
+                        responseAck((SIPRequest) sipMsgInfo.getEvt().getRequest(), Response.OK);
                     } catch (SipException | InvalidArgumentException | ParseException e) {
                         logger.error("[处理报警通知], 回复200OK失败", e);
                     }
@@ -183,7 +185,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
                             deviceAlarm.setAlarmType(getText(sipMsgInfo.getRootElement().element("Info"), "AlarmType"));
                         }
                     }
-                    logger.info("[收到报警通知]内容:{}", JSONObject.toJSON(deviceAlarm));
+                    logger.info("[收到报警通知]内容:{}", JSON.toJSONString(deviceAlarm));
                     if ("7".equals(deviceAlarm.getAlarmMethod()) ) {
                         // 发送给平台的报警信息。 发送redis通知
                         AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
@@ -216,7 +218,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
         logger.info("收到来自平台[{}]的报警通知", parentPlatform.getServerGBId());
         // 回复200 OK
         try {
-            responseAck(getServerTransaction(evt), Response.OK);
+            responseAck((SIPRequest) evt.getRequest(), Response.OK);
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[命令发送失败] 国标级联 报警通知回复: {}", e.getMessage());
         }

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

@@ -10,6 +10,7 @@ import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.utils.DateUtil;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -54,7 +55,7 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
         }
         // 回复200 OK
         try {
-            responseAck(getServerTransaction(evt), Response.OK);
+            responseAck((SIPRequest) evt.getRequest(), Response.OK);
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[命令发送失败] 国标级联 心跳回复: {}", e.getMessage());
         }

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

@@ -14,6 +14,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessag
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -67,7 +68,7 @@ public class MediaStatusNotifyMessageHandler extends SIPRequestProcessorParent i
 
         // 回复200 OK
         try {
-            responseAck(getServerTransaction(evt), Response.OK);
+             responseAck((SIPRequest) evt.getRequest(), Response.OK);
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[命令发送失败] 国标级联 录像流推送完毕,回复200OK: {}", e.getMessage());
         }

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

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.cmd;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
@@ -11,6 +11,7 @@ import com.genersoft.iot.vmp.service.IDeviceChannelService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.utils.DateUtil;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.DocumentException;
 import org.dom4j.Element;
 import org.slf4j.Logger;
@@ -83,7 +84,7 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen
                         if (rootElementAfterCharset == null) {
                             try {
                                 logger.warn("[ 移动设备位置数据通知 ] content cannot be null, {}", sipMsgInfo.getEvt().getRequest());
-                                responseAck(getServerTransaction(sipMsgInfo.getEvt()), Response.BAD_REQUEST);
+                                responseAck((SIPRequest) sipMsgInfo.getEvt().getRequest(), Response.BAD_REQUEST);
                             } catch (SipException | InvalidArgumentException | ParseException e) {
                                 logger.error("[命令发送失败] 移动设备位置数据通知 内容为空: {}", e.getMessage());
                             }
@@ -138,7 +139,7 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen
                         storager.updateChannelPosition(deviceChannel);
                         //回复 200 OK
                         try {
-                            responseAck(getServerTransaction(sipMsgInfo.getEvt()), Response.OK);
+                            responseAck((SIPRequest) sipMsgInfo.getEvt().getRequest(), Response.OK);
                         } catch (SipException | InvalidArgumentException | ParseException e) {
                             logger.error("[命令发送失败] 移动设备位置数据回复200: {}", e.getMessage());
                         }

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

@@ -9,6 +9,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -58,7 +59,7 @@ public class AlarmQueryMessageHandler extends SIPRequestProcessorParent implemen
 
         logger.info("不支持alarm查询");
         try {
-            responseAck(getServerTransaction(evt), Response.NOT_FOUND, "not support alarm query");
+             responseAck((SIPRequest) evt.getRequest(), Response.NOT_FOUND, "not support alarm query");
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[命令发送失败] 国标级联 alarm查询回复200OK: {}", e.getMessage());
         }

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

@@ -9,6 +9,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -66,7 +67,7 @@ public class CatalogQueryMessageHandler extends SIPRequestProcessorParent implem
         FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
         try {
             // 回复200 OK
-            responseAck(getServerTransaction(evt), Response.OK);
+             responseAck((SIPRequest) evt.getRequest(), Response.OK);
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[命令发送失败] 国标级联 目录查询回复200OK: {}", e.getMessage());
         }

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

@@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -48,7 +49,7 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp
         FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
         try {
             // 回复200 OK
-            responseAck(getServerTransaction(evt), Response.OK);
+            responseAck((SIPRequest) evt.getRequest(), Response.OK);
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[命令发送失败] DeviceInfo查询回复: {}", e.getMessage());
         }

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

@@ -9,6 +9,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.QueryMessageHandler;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -61,7 +62,7 @@ public class DeviceStatusQueryMessageHandler extends SIPRequestProcessorParent i
         FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
         // 回复200 OK
         try {
-            responseAck(getServerTransaction(evt), Response.OK);
+             responseAck((SIPRequest) evt.getRequest(), Response.OK);
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[命令发送失败] 国标级联 DeviceStatus查询回复200OK: {}", e.getMessage());
         }

+ 7 - 9
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/RecordInfoQueryMessageHandler.java

@@ -12,6 +12,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.query.Q
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -21,9 +22,7 @@ import org.springframework.stereotype.Component;
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.RequestEvent;
-import javax.sip.ServerTransaction;
 import javax.sip.SipException;
-import javax.sip.header.FromHeader;
 import javax.sip.message.Response;
 import java.text.ParseException;
 import java.util.List;
@@ -68,8 +67,7 @@ public class RecordInfoQueryMessageHandler extends SIPRequestProcessorParent imp
     @Override
     public void handForPlatform(RequestEvent evt, ParentPlatform parentPlatform, Element rootElement) {
 
-        FromHeader fromHeader = (FromHeader) evt.getRequest().getHeader(FromHeader.NAME);
-        ServerTransaction serverTransaction = getServerTransaction(evt);
+        SIPRequest request = (SIPRequest) evt.getRequest();
         Element snElement = rootElement.element("SN");
         int sn = Integer.parseInt(snElement.getText());
         Element deviceIDElement = rootElement.element("DeviceID");
@@ -104,7 +102,7 @@ public class RecordInfoQueryMessageHandler extends SIPRequestProcessorParent imp
             // 接收录像数据
             recordEndEventListener.addEndEventHandler(deviceChannel.getDeviceId(), channelId, (recordInfo)->{
                 try {
-                    cmderFroPlatform.recordInfo(deviceChannel, parentPlatform, fromHeader.getTag(), recordInfo);
+                    cmderFroPlatform.recordInfo(deviceChannel, parentPlatform, request.getFromTag(), recordInfo);
                 } catch (SipException | InvalidArgumentException | ParseException e) {
                     logger.error("[命令发送失败] 国标级联 回复录像数据: {}", e.getMessage());
                 }
@@ -114,14 +112,14 @@ public class RecordInfoQueryMessageHandler extends SIPRequestProcessorParent imp
                         DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(endTime), sn, secrecy, type, (eventResult -> {
                             // 回复200 OK
                             try {
-                                responseAck(serverTransaction, Response.OK);
+                                responseAck(request, Response.OK);
                             } catch (SipException | InvalidArgumentException | ParseException e) {
                                 logger.error("[命令发送失败] 录像查询回复: {}", e.getMessage());
                             }
                         }),(eventResult -> {
                             // 查询失败
                             try {
-                                responseAck(serverTransaction, eventResult.statusCode, eventResult.msg);
+                                responseAck(request, eventResult.statusCode, eventResult.msg);
                             } catch (SipException | InvalidArgumentException | ParseException e) {
                                 logger.error("[命令发送失败] 录像查询回复: {}", e.getMessage());
                             }
@@ -133,13 +131,13 @@ public class RecordInfoQueryMessageHandler extends SIPRequestProcessorParent imp
         }else if (channelSources.get(1).getCount() > 0) { // 直播流
             // TODO
             try {
-                responseAck(serverTransaction, Response.NOT_IMPLEMENTED); // 回复未实现
+                responseAck(request, Response.NOT_IMPLEMENTED); // 回复未实现
             } catch (SipException | InvalidArgumentException | ParseException e) {
                 logger.error("[命令发送失败] 录像查询: {}", e.getMessage());
             }
         }else { // 错误的请求
             try {
-                responseAck(serverTransaction, Response.BAD_REQUEST);
+                responseAck(request, Response.BAD_REQUEST);
             } catch (SipException | InvalidArgumentException | ParseException e) {
                 logger.error("[命令发送失败] 录像查询: {}", e.getMessage());
             }

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

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;

+ 18 - 4
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java

@@ -12,6 +12,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -21,7 +22,6 @@ import org.springframework.stereotype.Component;
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.RequestEvent;
-import javax.sip.ServerTransaction;
 import javax.sip.SipException;
 import javax.sip.message.Response;
 import java.text.ParseException;
@@ -52,17 +52,31 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i
     public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
         try {
             String channelId = getText(rootElement, "DeviceID");
-            ServerTransaction serverTransaction = getServerTransaction(evt);
+            String key = DeferredResultHolder.CALLBACK_CMD_BROADCAST + device.getDeviceId() + channelId;
+
+            // 此处是对本平台发出Broadcast指令的应答
+            JSONObject json = new JSONObject();
+            XmlUtil.node2Json(rootElement, json);
+            if (logger.isDebugEnabled()) {
+                logger.debug(json.toJSONString());
+            }
+            RequestMessage msg = new RequestMessage();
+            msg.setKey(key);
+            msg.setData(json);
+            deferredResultHolder.invokeAllResult(msg);
+
+
             if (!audioBroadcastManager.exit(device.getDeviceId(), channelId)) {
                 // 回复410
-                responseAck(serverTransaction, Response.GONE);
+                responseAck((SIPRequest) evt.getRequest(), Response.GONE);
                 return;
             }
             logger.info("收到语音广播的回复:{}/{}", device.getDeviceId(), channelId );
             AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(device.getDeviceId(), channelId);
             audioBroadcastCatch.setStatus(AudioBroadcastCatchStatus.WaiteInvite);
             audioBroadcastManager.update(audioBroadcastCatch);
-            responseAck(serverTransaction, Response.OK);
+            // 回复200 OK
+            responseAck((SIPRequest) evt.getRequest(), Response.OK);
         } catch (ParseException | SipException | InvalidArgumentException e) {
             logger.error("[命令发送失败] 国标级联 语音喊话: {}", e.getMessage());
         }

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

@@ -14,6 +14,7 @@ import com.genersoft.iot.vmp.gb28181.utils.NumericUtil;
 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.DocumentException;
 import org.dom4j.Element;
 import org.slf4j.Logger;
@@ -27,7 +28,6 @@ import org.springframework.util.StringUtils;
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.RequestEvent;
-import javax.sip.ServerTransaction;
 import javax.sip.SipException;
 import javax.sip.message.Response;
 import java.text.ParseException;
@@ -71,9 +71,8 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
     public void handForDevice(RequestEvent evt, Device device, Element element) {
         taskQueue.offer(new HandlerCatchData(evt, device, element));
         // 回复200 OK
-        ServerTransaction serverTransaction = getServerTransaction(evt);
         try {
-            responseAck(serverTransaction, Response.OK);
+            responseAck((SIPRequest) evt.getRequest(), Response.OK);
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[命令发送失败] 目录查询回复: {}", e.getMessage());
         }

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

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
@@ -10,6 +10,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -52,7 +53,7 @@ public class ConfigDownloadResponseMessageHandler extends SIPRequestProcessorPar
         String key = DeferredResultHolder.CALLBACK_CMD_CONFIGDOWNLOAD + device.getDeviceId() + channelId;
         try {
             // 回复200 OK
-            responseAck(getServerTransaction(evt), Response.OK);
+            responseAck((SIPRequest) evt.getRequest(), Response.OK);
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[命令发送失败] 设备配置查询: {}", e.getMessage());
         }

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

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;

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

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
@@ -9,6 +9,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorP
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -46,7 +47,7 @@ public class DeviceControlResponseMessageHandler extends SIPRequestProcessorPare
     public void handForDevice(RequestEvent evt, Device device, Element element) {
         // 此处是对本平台发出DeviceControl指令的应答
         try {
-            responseAck(getServerTransaction(evt), Response.OK);
+             responseAck((SIPRequest) evt.getRequest(), Response.OK);
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[命令发送失败] 国标级联 设备控制: {}", e.getMessage());
         }

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

@@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.respons
 import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.DocumentException;
 import org.dom4j.Element;
 import org.slf4j.Logger;
@@ -21,11 +22,9 @@ import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.RequestEvent;
-import javax.sip.ServerTransaction;
 import javax.sip.SipException;
 import javax.sip.message.Response;
 import java.text.ParseException;
@@ -44,20 +43,9 @@ public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent
     @Autowired
     private ResponseMessageHandler responseMessageHandler;
 
-    @Autowired
-    private IVideoManagerStorage storager;
-
-    @Autowired
-    private IRedisCatchStorage redisCatchStorage;
-
     @Autowired
     private DeferredResultHolder deferredResultHolder;
 
-    @Autowired
-    private SipConfig config;
-
-    @Autowired
-    private EventPublisher publisher;
 
     @Autowired
     private IDeviceService deviceService;
@@ -75,14 +63,14 @@ public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent
             logger.warn("[接收到DeviceInfo应答消息,但是设备已经离线]:" + (device != null ? device.getDeviceId():"" ));
             return;
         }
-        ServerTransaction serverTransaction = getServerTransaction(evt);
+        SIPRequest request = (SIPRequest) evt.getRequest();
         try {
             rootElement = getRootElement(evt, device.getCharset());
 
-        if (rootElement == null) {
+            if (rootElement == null) {
                 logger.warn("[ 接收到DeviceInfo应答消息 ] content cannot be null, {}", evt.getRequest());
                 try {
-                    responseAck(serverTransaction, Response.BAD_REQUEST);
+                    responseAck((SIPRequest) evt.getRequest(), Response.BAD_REQUEST);
                 } catch (SipException | InvalidArgumentException | ParseException e) {
                     logger.error("[命令发送失败] DeviceInfo应答消息 BAD_REQUEST: {}", e.getMessage());
                 }
@@ -110,7 +98,7 @@ public class DeviceInfoResponseMessageHandler extends SIPRequestProcessorParent
         }
         try {
             // 回复200 OK
-            responseAck(serverTransaction, Response.OK);
+            responseAck(request, Response.OK);
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[命令发送失败] DeviceInfo应答消息 200: {}", e.getMessage());
         }

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

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
 
-import com.alibaba.fastjson.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.ParentPlatform;
@@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.respons
 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
 import com.genersoft.iot.vmp.service.IDeviceService;
 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.Element;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -59,7 +60,7 @@ public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParen
         }
         // 回复200 OK
         try {
-            responseAck(getServerTransaction(evt), Response.OK);
+             responseAck((SIPRequest) evt.getRequest(), Response.OK);
         } catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[命令发送失败] 国标级联 设备状态应答回复200OK: {}", e.getMessage());
         }

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

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
@@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.utils.GpsUtil;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.DocumentException;
 import org.dom4j.Element;
 import org.slf4j.Logger;
@@ -21,11 +22,9 @@ import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.RequestEvent;
-import javax.sip.ServerTransaction;
 import javax.sip.SipException;
 import javax.sip.message.Response;
 import java.text.ParseException;
@@ -64,15 +63,14 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar
 
     @Override
     public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
-
-        ServerTransaction serverTransaction = getServerTransaction(evt);
+        SIPRequest request = (SIPRequest) evt.getRequest();
 
         try {
             rootElement = getRootElement(evt, device.getCharset());
             if (rootElement == null) {
                 logger.warn("[ 移动设备位置数据查询回复 ] content cannot be null, {}", evt.getRequest());
                 try {
-                    responseAck(serverTransaction, Response.BAD_REQUEST);
+                    responseAck(request, Response.BAD_REQUEST);
                 } catch (SipException | InvalidArgumentException | ParseException e) {
                     logger.error("[命令发送失败] 移动设备位置数据查询 BAD_REQUEST: {}", e.getMessage());
                 }
@@ -138,7 +136,7 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar
             redisCatchStorage.sendMobilePositionMsg(jsonObject);
             //回复 200 OK
             try {
-                responseAck(serverTransaction, Response.OK);
+                responseAck(request, Response.OK);
             } catch (SipException | InvalidArgumentException | ParseException e) {
                 logger.error("[命令发送失败] 移动设备位置数据查询 200: {}", e.getMessage());
             }

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

@@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler;
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.DocumentException;
 import org.dom4j.Element;
 import org.slf4j.Logger;
@@ -17,7 +18,6 @@ import org.springframework.stereotype.Component;
 
 import javax.sip.InvalidArgumentException;
 import javax.sip.RequestEvent;
-import javax.sip.ServerTransaction;
 import javax.sip.SipException;
 import javax.sip.message.Response;
 import java.text.ParseException;
@@ -51,7 +51,7 @@ public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent
     @Override
     public void handForDevice(RequestEvent evt, Device device, Element element) {
 
-        ServerTransaction serverTransaction = getServerTransaction(evt);
+        SIPRequest request = (SIPRequest) evt.getRequest();
 
         try {
              Element rootElement = getRootElement(evt, device.getCharset());
@@ -59,7 +59,7 @@ public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent
             if (rootElement == null) {
                 logger.warn("[ 设备预置位查询应答 ] content cannot be null, {}", evt.getRequest());
                 try {
-                    responseAck(serverTransaction, Response.BAD_REQUEST);
+                    responseAck(request, Response.BAD_REQUEST);
                 } catch (InvalidArgumentException | ParseException | SipException e) {
                     logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage());
                 }
@@ -72,7 +72,7 @@ public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent
             String key = DeferredResultHolder.CALLBACK_CMD_PRESETQUERY + deviceId;
             if (snElement == null || presetListNumElement == null) {
                 try {
-                    responseAck(serverTransaction, Response.BAD_REQUEST, "xml error");
+                    responseAck(request, Response.BAD_REQUEST, "xml error");
                 } catch (InvalidArgumentException | ParseException | SipException e) {
                     logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage());
                 }
@@ -103,7 +103,7 @@ public class PresetQueryResponseMessageHandler extends SIPRequestProcessorParent
             requestMessage.setData(presetQuerySipReqList);
             deferredResultHolder.invokeAllResult(requestMessage);
             try {
-                responseAck(serverTransaction, Response.OK);
+                responseAck(request, Response.OK);
             } catch (InvalidArgumentException | ParseException | SipException e) {
                 logger.error("[命令发送失败] 设备预置位查询应答处理: {}", e.getMessage());
             }

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

@@ -10,6 +10,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessag
 import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
+import gov.nist.javax.sip.message.SIPRequest;
 import org.dom4j.DocumentException;
 import org.dom4j.Element;
 import org.slf4j.Logger;
@@ -71,7 +72,7 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
     public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
         try {
             // 回复200 OK
-            responseAck(getServerTransaction(evt), Response.OK);
+             responseAck((SIPRequest) evt.getRequest(), Response.OK);
         }catch (SipException | InvalidArgumentException | ParseException e) {
             logger.error("[命令发送失败] 国标级联 国标录像: {}", e.getMessage());
         }

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

@@ -1,10 +1,12 @@
 package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
 
 import com.genersoft.iot.vmp.conf.SipConfig;
+import com.genersoft.iot.vmp.gb28181.SipLayer;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
+import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderProvider;
 import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
@@ -48,34 +50,19 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
 	private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
 	private final String method = "INVITE";
 
-	@Autowired
-	private VideoStreamSessionManager streamSession;
-
 	@Autowired
 	private SIPProcessorObserver sipProcessorObserver;
 
-	@Autowired
-	private SipConfig sipConfig;
-
-	@Autowired
-	private SipFactory sipFactory;
 
 	@Autowired
-	private GitUtil gitUtil;
+	private SipLayer sipLayer;
 
 	@Autowired
-	private ISIPCommander commander;
-
-	@Autowired
-	private IDeviceService deviceService;
+	private SIPSender sipSender;
 
 	@Autowired
 	private SIPRequestHeaderProvider headerProvider;
 
-	@Autowired
-	@Qualifier(value="udpSipProvider")
-	private SipProviderImpl udpSipProvider;
-
 
 	@Override
 	public void afterPropertiesSet() throws Exception {
@@ -117,12 +104,12 @@ public class InviteResponseProcessor extends SIPResponseProcessorAbstract {
 				} else {
 					sdp = SdpFactory.getInstance().createSessionDescription(contentString);
 				}
-				SipURI requestUri = sipFactory.createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort());
-				Request reqAck = headerProvider.createAckRequest(requestUri, response);
 
-				logger.info("[回复ack] {}-> {}:{} ", sdp.getOrigin().getUsername(), event.getRemoteIpAddress(), event.getRemotePort());
-				commander.transmitRequest(response.getTopmostViaHeader().getTransport(), reqAck, null, null);
+				SipURI requestUri = sipLayer.getSipFactory().createAddressFactory().createSipURI(sdp.getOrigin().getUsername(), event.getRemoteIpAddress() + ":" + event.getRemotePort());
+				Request reqAck = headerProvider.createAckRequest(response.getLocalAddress().getHostAddress(), requestUri, response);
 
+				logger.info("[回复ack] {}-> {}:{} ", sdp.getOrigin().getUsername(), event.getRemoteIpAddress(), event.getRemotePort());
+				sipSender.transmitRequest( response.getLocalAddress().getHostAddress(), reqAck);
 			}
 		} catch (InvalidArgumentException | ParseException | SipException | SdpParseException e) {
 			logger.info("[点播回复ACK],异常:", e );

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

@@ -100,7 +100,7 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
 			if (platformRegisterInfo.isRegister()) {
 				platformService.online(parentPlatform);
 			}else {
-				platformService.offline(parentPlatform);
+				platformService.offline(parentPlatform, false);
 			}
 
 			// 注册/注销成功移除缓存的信息

+ 10 - 4
src/main/java/com/genersoft/iot/vmp/gb28181/utils/SipUtils.java

@@ -4,6 +4,7 @@ import com.genersoft.iot.vmp.utils.GitUtil;
 import gov.nist.javax.sip.address.AddressImpl;
 import gov.nist.javax.sip.address.SipUri;
 import gov.nist.javax.sip.header.Subject;
+import org.springframework.util.ObjectUtils;
 
 import javax.sip.PeerUnavailableException;
 import javax.sip.SipFactory;
@@ -52,10 +53,15 @@ public class SipUtils {
 
     public static UserAgentHeader createUserAgentHeader(SipFactory sipFactory, GitUtil gitUtil) throws PeerUnavailableException, ParseException {
         List<String> agentParam = new ArrayList<>();
-        agentParam.add("WVP-Pro v");
-        if (gitUtil != null && gitUtil.getCommitTime() != null) {
-            agentParam.add(gitUtil.getBuildVersion() + ".");
-            agentParam.add(gitUtil.getCommitTime());
+        agentParam.add("WVP-Pro ");
+        if (gitUtil != null ) {
+            if (!ObjectUtils.isEmpty(gitUtil.getBuildVersion())) {
+                agentParam.add("v");
+                agentParam.add(gitUtil.getBuildVersion() + ".");
+            }
+            if (!ObjectUtils.isEmpty(gitUtil.getCommitTime())) {
+                agentParam.add(gitUtil.getCommitTime());
+            }
         }
         return sipFactory.createHeaderFactory().createUserAgentHeader(agentParam);
     }

+ 6 - 2
src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java

@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.gb28181.utils;
 
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.gb28181.bean.Device;
 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
 import com.genersoft.iot.vmp.gb28181.bean.TreeType;
@@ -298,6 +298,10 @@ public class XmlUtil {
             }else {
                 deviceChannel.setParentId(parentId);
             }
+            // 兼容设备通道信息中自己为自己父节点的情况
+            if (deviceChannel.getParentId().equals(deviceChannel.getChannelId())) {
+                deviceChannel.setParentId(null);
+            }
         }
         deviceChannel.setBusinessGroupId(businessGroupID);
         if (channelType.equals(ChannelType.BusinessGroup) || channelType.equals(ChannelType.VirtualOrganization)) {

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

@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.media.zlm;
 
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import okhttp3.*;
 import okhttp3.logging.HttpLoggingInterceptor;

+ 265 - 377
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java

@@ -1,11 +1,7 @@
 package com.genersoft.iot.vmp.media.zlm;
 
-import java.text.ParseException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.common.StreamInfo;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
@@ -13,6 +9,13 @@ import com.genersoft.iot.vmp.gb28181.bean.*;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
+import com.genersoft.iot.vmp.media.zlm.dto.HookType;
+import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo;
+import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
+import com.genersoft.iot.vmp.media.zlm.dto.hook.*;
 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
 import com.genersoft.iot.vmp.media.zlm.dto.*;
@@ -27,18 +30,15 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.util.ObjectUtils;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.RestController;
-
-import com.alibaba.fastjson.JSONObject;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.sip.InvalidArgumentException;
 import javax.sip.SipException;
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
 
 /**    
  * @description:针对 ZLMediaServer的hook事件监听
@@ -118,17 +118,20 @@ public class ZLMHttpHookListener {
 	 */
 	@ResponseBody
 	@PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")
-	public JSONObject onServerKeepalive(@RequestBody JSONObject json){
+	public JSONObject onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param){
 
-		logger.info("[ ZLM HOOK ]on_server_keepalive API调用,参数:" + json.toString());
-		String mediaServerId = json.getString("mediaServerId");
-		List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
-		if (subscribes != null  && subscribes.size() > 0) {
-			for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
-				subscribe.response(null, json);
+		logger.info("[ZLM HOOK] 收到zlm心跳:" + param.getMediaServerId());
+
+		taskExecutor.execute(()->{
+			List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
+			JSONObject json = (JSONObject) JSON.toJSON(param);
+			if (subscribes != null  && subscribes.size() > 0) {
+				for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
+					subscribe.response(null, json);
+				}
 			}
-		}
-		mediaServerService.updateMediaServerKeepalive(mediaServerId, json.getJSONObject("data"));
+		});
+		mediaServerService.updateMediaServerKeepalive(param.getMediaServerId(), param.getData());
 
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
@@ -136,43 +139,6 @@ public class ZLMHttpHookListener {
 
 		return ret;
 	}
-
-	/**
-	 * 流量统计事件,播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件,阈值通过配置文件general.flowThreshold配置;此事件对回复不敏感。
-	 *  
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_flow_report", produces = "application/json;charset=UTF-8")
-	public JSONObject onFlowReport(@RequestBody JSONObject json){
-		
-		if (logger.isDebugEnabled()) {
-			logger.debug("[ ZLM HOOK ]on_flow_report API调用,参数:" + json.toString());
-		}
-		JSONObject ret = new JSONObject();
-		ret.put("code", 0);
-		ret.put("msg", "success");
-		return ret;
-	}
-	
-	/**
-	 * 访问http文件服务器上hls之外的文件时触发。
-	 *  
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_http_access", produces = "application/json;charset=UTF-8")
-	public JSONObject onHttpAccess(@RequestBody JSONObject json){
-		
-		if (logger.isDebugEnabled()) {
-			logger.debug("[ ZLM HOOK ]on_http_access API 调用,参数:" + json.toString());
-		}
-		String mediaServerId = json.getString("mediaServerId");
-		JSONObject ret = new JSONObject();
-		ret.put("code", 0);
-		ret.put("err", "");
-		ret.put("path", "");
-		ret.put("second", 600);
-		return ret;
-	}
 	
 	/**
 	 * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。
@@ -181,20 +147,21 @@ public class ZLMHttpHookListener {
 	@ResponseBody
 	@PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
 	public JSONObject onPlay(@RequestBody OnPlayHookParam param){
-
-		JSONObject json = (JSONObject)JSON.toJSON(param);
-
 		if (logger.isDebugEnabled()) {
-			logger.debug("[ ZLM HOOK ]on_play API调用,参数:" + JSON.toJSONString(param));
+			logger.debug("[ZLM HOOK] 播放鉴权:{}->{}" + param.getMediaServerId(), param);
 		}
 		String mediaServerId = param.getMediaServerId();
-		ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json);
-		if (subscribe != null ) {
-			MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
-			if (mediaInfo != null) {
-				subscribe.response(mediaInfo, json);
+
+		taskExecutor.execute(()->{
+			JSONObject json = (JSONObject) JSON.toJSON(param);
+			ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_play, json);
+			if (subscribe != null ) {
+				MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+				if (mediaInfo != null) {
+					subscribe.response(mediaInfo, json);
+				}
 			}
-		}
+		});
 		JSONObject ret = new JSONObject();
 		if (!"rtp".equals(param.getApp())) {
 			Map<String, String> paramMap = urlParamToMap(param.getParams());
@@ -221,46 +188,49 @@ public class ZLMHttpHookListener {
 
 		JSONObject json = (JSONObject) JSON.toJSON(param);
 
-		logger.info("[ ZLM HOOK ]on_publish API调用,参数:" + json.toString());
+		logger.info("[ZLM HOOK]推流鉴权:{}->{}",  param.getMediaServerId(), param);
 		JSONObject ret = new JSONObject();
 		String mediaServerId = json.getString("mediaServerId");
 		MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+
 		if (!"rtp".equals(param.getApp())) {
-			// 推流鉴权
-			if (param.getParams() == null) {
-				logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");
-				ret.put("code", 401);
-				ret.put("msg", "Unauthorized");
-				return ret;
-			}
-			Map<String, String> paramMap = urlParamToMap(param.getParams());
-			String sign = paramMap.get("sign");
-			if (sign == null) {
-				logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");
-				ret.put("code", 401);
-				ret.put("msg", "Unauthorized");
-				return ret;
-			}
-			// 推流自定义播放鉴权码
-			String callId = paramMap.get("callId");
-			// 鉴权配置
-			boolean hasAuthority = userService.checkPushAuthority(callId, sign);
-			if (!hasAuthority) {
-				logger.info("推流鉴权失败: sign 无权限: callId={}. sign={}", callId, sign);
-				ret.put("code", 401);
-				ret.put("msg", "Unauthorized");
-				return ret;
-			}
-			StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
-			streamAuthorityInfo.setCallId(callId);
-			streamAuthorityInfo.setSign(sign);
-			// 鉴权通过
-			redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
-			// 通知assist新的callId
-			if (mediaInfo != null && mediaInfo.getRecordAssistPort() > 0) {
-				taskExecutor.execute(()->{
-					assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null);
-				});
+			if (userSetting.getPushAuthority()) {
+				// 推流鉴权
+				if (param.getParams() == null) {
+					logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");
+					ret.put("code", 401);
+					ret.put("msg", "Unauthorized");
+					return ret;
+				}
+				Map<String, String> paramMap = urlParamToMap(param.getParams());
+				String sign = paramMap.get("sign");
+				if (sign == null) {
+					logger.info("推流鉴权失败: 缺少不要参数:sign=md5(user表的pushKey)");
+					ret.put("code", 401);
+					ret.put("msg", "Unauthorized");
+					return ret;
+				}
+				// 推流自定义播放鉴权码
+				String callId = paramMap.get("callId");
+				// 鉴权配置
+				boolean hasAuthority = userService.checkPushAuthority(callId, sign);
+				if (!hasAuthority) {
+					logger.info("推流鉴权失败: sign 无权限: callId={}. sign={}", callId, sign);
+					ret.put("code", 401);
+					ret.put("msg", "Unauthorized");
+					return ret;
+				}
+				StreamAuthorityInfo streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
+				streamAuthorityInfo.setCallId(callId);
+				streamAuthorityInfo.setSign(sign);
+				// 鉴权通过
+				redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
+				// 通知assist新的callId
+				if (mediaInfo != null && mediaInfo.getRecordAssistPort() > 0) {
+					taskExecutor.execute(()->{
+						assistRESTfulUtils.addStreamCallInfo(mediaInfo, param.getApp(), param.getStream(), callId, null);
+					});
+				}
 			}
 		}else {
 			zlmMediaListManager.sendStreamEvent(param.getApp(),param.getStream(), param.getMediaServerId());
@@ -268,21 +238,22 @@ public class ZLMHttpHookListener {
 
 		ret.put("code", 0);
 		ret.put("msg", "success");
-		ret.put("enable_hls", true);
+
 		if (!"rtp".equals(param.getApp())) {
 			ret.put("enable_audio", true);
 		}
 
-
-		ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
-		if (subscribe != null) {
-			if (mediaInfo != null) {
-				subscribe.response(mediaInfo, json);
-			}else {
-				ret.put("code", 1);
-				ret.put("msg", "zlm not register");
+		taskExecutor.execute(()->{
+			ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_publish, json);
+			if (subscribe != null) {
+				if (mediaInfo != null) {
+					subscribe.response(mediaInfo, json);
+				}else {
+					ret.put("code", 1);
+					ret.put("msg", "zlm not register");
+				}
 			}
-		}
+		});
 
 		if ("rtp".equals(param.getApp())) {
 			ret.put("enable_mp4", userSetting.getRecordSip());
@@ -302,175 +273,74 @@ public class ZLMHttpHookListener {
 				ret.put("mp4_max_second", 10);
 				ret.put("enable_mp4", true);
 				ret.put("enable_audio", true);
-
 			}
 		}
 		return ret;
 	}
-
-
-
-	/**
-	 * 录制mp4完成后通知事件;此事件对回复不敏感。
-	 *  
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8")
-	public JSONObject onRecordMp4(@RequestBody JSONObject json){
-		
-		if (logger.isDebugEnabled()) {
-			logger.debug("[ ZLM HOOK ]on_record_mp4 API调用,参数:" + json.toString());
-		}
-		String mediaServerId = json.getString("mediaServerId");
-		JSONObject ret = new JSONObject();
-		ret.put("code", 0);
-		ret.put("msg", "success");
-		return ret;
-	}
-	/**
-	 * 录制hls完成后通知事件;此事件对回复不敏感。
-	 *
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_record_ts", produces = "application/json;charset=UTF-8")
-	public JSONObject onRecordTs(@RequestBody JSONObject json){
-
-		if (logger.isDebugEnabled()) {
-			logger.debug("[ ZLM HOOK ]on_record_ts API调用,参数:" + json.toString());
-		}
-		String mediaServerId = json.getString("mediaServerId");
-		JSONObject ret = new JSONObject();
-		ret.put("code", 0);
-		ret.put("msg", "success");
-		return ret;
-	}
 	
 	/**
-	 * rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件。
-	 *  
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_rtsp_realm", produces = "application/json;charset=UTF-8")
-	public JSONObject onRtspRealm(@RequestBody JSONObject json){
-		
-		if (logger.isDebugEnabled()) {
-			logger.debug("[ ZLM HOOK ]on_rtsp_realm API调用,参数:" + json.toString());
-		}
-		String mediaServerId = json.getString("mediaServerId");
-		JSONObject ret = new JSONObject();
-		ret.put("code", 0);
-		ret.put("realm", "");
-		return ret;
-	}
-	
-	
-	/**
-	 * 该rtsp流是否开启rtsp专用方式的鉴权事件,开启后才会触发on_rtsp_auth事件。需要指出的是rtsp也支持url参数鉴权,它支持两种方式鉴权。
-	 *  
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_rtsp_auth", produces = "application/json;charset=UTF-8")
-	public JSONObject onRtspAuth(@RequestBody JSONObject json){
-		
-		if (logger.isDebugEnabled()) {
-			logger.debug("[ ZLM HOOK ]on_rtsp_auth API调用,参数:" + json.toString());
-		}
-		String mediaServerId = json.getString("mediaServerId");
-		JSONObject ret = new JSONObject();
-		ret.put("code", 0);
-		ret.put("encrypted", false);
-		ret.put("passwd", "test");
-		return ret;
-	}
-	
-	/**
-	 * shell登录鉴权,ZLMediaKit提供简单的telnet调试方式,使用telnet 127.0.0.1 9000能进入MediaServer进程的shell界面。
+	 * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
 	 *  
 	 */
 	@ResponseBody
-	@PostMapping(value = "/on_shell_login", produces = "application/json;charset=UTF-8")
-	public JSONObject onShellLogin(@RequestBody JSONObject json){
-		
-		if (logger.isDebugEnabled()) {
-			logger.debug("[ ZLM HOOK ]on_shell_login API调用,参数:" + json.toString());
-		}
-		String mediaServerId = json.getString("mediaServerId");
-		ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_shell_login, json);
-		if (subscribe != null ) {
-			MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
-			if (mediaInfo != null) {
-				subscribe.response(mediaInfo, json);
-			}
+	@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
+	public JSONObject onStreamChanged(@RequestBody OnStreamChangedHookParam param){
 
+		if (param.isRegist()) {
+			logger.info("[ZLM HOOK] 流注册, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
+		}else {
+			logger.info("[ZLM HOOK] 流注销, {}->{}->{}/{}", param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
 		}
 
-		JSONObject ret = new JSONObject();
-		ret.put("code", 0);
-		ret.put("msg", "success");
-		return ret;
-	}
-	
-	/**
-	 * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
-	 *  
-	 */
-	@ResponseBody
-	@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
-	public JSONObject onStreamChanged(@RequestBody MediaItem item){
-
-		logger.info("[ ZLM HOOK ]on_stream_changed API调用,参数:" + JSONObject.toJSONString(item));
-		String mediaServerId = item.getMediaServerId();
-		JSONObject json = (JSONObject) JSON.toJSON(item);
-		ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
-		if (subscribe != null ) {
-			MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
-			if (mediaInfo != null) {
-				subscribe.response(mediaInfo, json);
+
+		JSONObject json = (JSONObject) JSON.toJSON(param);
+		taskExecutor.execute(()->{
+			ZlmHttpHookSubscribe.Event subscribe = this.subscribe.sendNotify(HookType.on_stream_changed, json);
+			if (subscribe != null ) {
+				MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
+				if (mediaInfo != null) {
+					subscribe.response(mediaInfo, json);
+				}
 			}
-		}
-		// 流消失移除redis play
-		String app = item.getApp();
-		String stream = item.getStream();
-		String schema = item.getSchema();
-		List<MediaItem.MediaTrack> tracks = item.getTracks();
-		boolean regist = item.isRegist();
-		if (regist) {
-			if (item.getOriginType() == OriginType.RTMP_PUSH.ordinal()
-					|| item.getOriginType() == OriginType.RTSP_PUSH.ordinal()
-					|| item.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
-
-				StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream);
-				if (streamAuthorityInfo == null) {
-					streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(item);
-				}else {
-					streamAuthorityInfo.setOriginType(item.getOriginType());
-					streamAuthorityInfo.setOriginTypeStr(item.getOriginTypeStr());
+			// 流消失移除redis play
+			List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
+			if (param.isRegist()) {
+				if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
+						|| param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
+						|| param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
+
+					StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
+					if (streamAuthorityInfo == null) {
+						streamAuthorityInfo = StreamAuthorityInfo.getInstanceByHook(param);
+					}else {
+						streamAuthorityInfo.setOriginType(param.getOriginType());
+						streamAuthorityInfo.setOriginTypeStr(param.getOriginTypeStr());
+					}
+					redisCatchStorage.updateStreamAuthorityInfo(param.getApp(), param.getStream(), streamAuthorityInfo);
 				}
-				redisCatchStorage.updateStreamAuthorityInfo(app, stream, streamAuthorityInfo);
+			}else {
+				redisCatchStorage.removeStreamAuthorityInfo(param.getApp(), param.getStream());
 			}
-		}else {
-			redisCatchStorage.removeStreamAuthorityInfo(app, stream);
-		}
 
-		if ("rtsp".equals(schema)){
-			logger.info("on_stream_changed:注册->{}, app->{}, stream->{}", regist, app, stream);
-			if (regist) {
-				mediaServerService.addCount(mediaServerId);
+		if ("rtsp".equals(param.getSchema())){
+			logger.info("on_stream_changed:注册->{}, app->{}, stream->{}", param.isRegist(), param.getApp(), param.getStream());
+			if (param.isRegist()) {
+				mediaServerService.addCount(param.getMediaServerId());
 			}else {
-				mediaServerService.removeCount(mediaServerId);
+				mediaServerService.removeCount(param.getMediaServerId());
 			}
 			if (item.getOriginType() == OriginType.PULL.ordinal()
 					|| item.getOriginType() == OriginType.FFMPEG_PULL.ordinal()) {
 				// 设置拉流代理上线/离线
-				streamProxyService.updateStatus(regist, app, stream);
+				streamProxyService.updateStatus(param.isRegist(), app, param.getStream());
 			}
 			if ("rtp".equals(app) && !regist ) {
-				StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(stream);
+				StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(param.getStream());
 				if (streamInfo!=null){
 					redisCatchStorage.stopPlay(streamInfo);
 					storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
 				}else{
-					streamInfo = redisCatchStorage.queryPlayback(null, null, stream, null);
+					streamInfo = redisCatchStorage.queryPlayback(null, null, param.getStream(), null);
 					if (streamInfo != null) {
 						redisCatchStorage.stopPlayback(streamInfo.getDeviceID(), streamInfo.getChannelId(),
 								streamInfo.getStream(), null);
@@ -478,8 +348,8 @@ public class ZLMHttpHookListener {
 				}
 			}else if ("broadcast".equals(app)){
 				// 语音对讲推流  stream需要满足格式deviceId_channelId
-				if (regist && stream.indexOf("_") > 0) {
-					String[] streamArray = stream.split("_");
+				if (regist && param.getStream().indexOf("_") > 0) {
+					String[] streamArray = param.getStream().split("_");
 					if (streamArray.length == 2) {
 						String deviceId = streamArray[0];
 						String channelId = streamArray[1];
@@ -600,57 +470,57 @@ public class ZLMHttpHookListener {
 					String type = OriginType.values()[item.getOriginType()].getType();
 					MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
 
-					if (mediaServerItem != null){
-						if (regist) {
-							StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(app, stream);
-							String callId = null;
-							if (streamAuthorityInfo != null) {
-								callId = streamAuthorityInfo.getCallId();
-							}
-							StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaServerItem,
-									app, stream, tracks, callId);
-							item.setStreamInfo(streamInfoByAppAndStream);
-							redisCatchStorage.addStream(mediaServerItem, type, app, stream, item);
-							if (item.getOriginType() == OriginType.RTSP_PUSH.ordinal()
-									|| item.getOriginType() == OriginType.RTMP_PUSH.ordinal()
-									|| item.getOriginType() == OriginType.RTC_PUSH.ordinal() ) {
-								item.setSeverId(userSetting.getServerId());
-								zlmMediaListManager.addPush(item);
-							}
-						}else {
-							// 兼容流注销时类型从redis记录获取
-							MediaItem mediaItem = redisCatchStorage.getStreamInfo(app, stream, mediaServerId);
-							if (mediaItem != null) {
-								type = OriginType.values()[mediaItem.getOriginType()].getType();
-								redisCatchStorage.removeStream(mediaServerItem.getId(), type, app, stream);
-							}
-							GbStream gbStream = storager.getGbStream(app, stream);
-							if (gbStream != null) {
+						if (mediaServerItem != null){
+							if (param.isRegist()) {
+								StreamAuthorityInfo streamAuthorityInfo = redisCatchStorage.getStreamAuthorityInfo(param.getApp(), param.getStream());
+								String callId = null;
+								if (streamAuthorityInfo != null) {
+									callId = streamAuthorityInfo.getCallId();
+								}
+								StreamInfo streamInfoByAppAndStream = mediaService.getStreamInfoByAppAndStream(mediaServerItem,
+										param.getApp(), param.getStream(), tracks, callId);
+								param.setStreamInfo(streamInfoByAppAndStream);
+								redisCatchStorage.addStream(mediaServerItem, type, param.getApp(), param.getStream(), param);
+								if (param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
+										|| param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
+										|| param.getOriginType() == OriginType.RTC_PUSH.ordinal() ) {
+									param.setSeverId(userSetting.getServerId());
+									zlmMediaListManager.addPush(param);
+								}
+							}else {
+								// 兼容流注销时类型从redis记录获取
+								OnStreamChangedHookParam onStreamChangedHookParam = redisCatchStorage.getStreamInfo(param.getApp(), param.getStream(), param.getMediaServerId());
+								if (onStreamChangedHookParam != null) {
+									type = OriginType.values()[onStreamChangedHookParam.getOriginType()].getType();
+									redisCatchStorage.removeStream(mediaServerItem.getId(), type, param.getApp(), param.getStream());
+								}
+								GbStream gbStream = storager.getGbStream(param.getApp(), param.getStream());
+								if (gbStream != null) {
 //								eventPublisher.catalogEventPublishForStream(null, gbStream, CatalogEvent.OFF);
+								}
+								zlmMediaListManager.removeMedia(param.getApp(), param.getStream());
+							}
+							if (type != null) {
+								// 发送流变化redis消息
+								JSONObject jsonObject = new JSONObject();
+								jsonObject.put("serverId", userSetting.getServerId());
+								jsonObject.put("app", param.getApp());
+								jsonObject.put("stream", param.getStream());
+								jsonObject.put("register", param.isRegist());
+								jsonObject.put("mediaServerId", param.getMediaServerId());
+								redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
 							}
-							zlmMediaListManager.removeMedia(app, stream);
-						}
-						if (type != null) {
-							// 发送流变化redis消息
-							JSONObject jsonObject = new JSONObject();
-							jsonObject.put("serverId", userSetting.getServerId());
-							jsonObject.put("app", app);
-							jsonObject.put("stream", stream);
-							jsonObject.put("register", regist);
-							jsonObject.put("mediaServerId", mediaServerId);
-							redisCatchStorage.sendStreamChangeMsg(type, jsonObject);
 						}
 					}
 				}
-			}
-			if (!regist ) {
-				List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(stream);
-				if (sendRtpItems.size() > 0) {
-					for (SendRtpItem sendRtpItem : sendRtpItems) {
-						if (sendRtpItem.getApp().equals(app)) {
-							String platformId = sendRtpItem.getPlatformId();
-							ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
-							Device device = deviceService.queryDevice(platformId);
+				if (!param.isRegist()) {
+					List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
+					if (sendRtpItems.size() > 0) {
+						for (SendRtpItem sendRtpItem : sendRtpItems) {
+							if (sendRtpItem.getApp().equals(param.getApp())) {
+								String platformId = sendRtpItem.getPlatformId();
+								ParentPlatform platform = storager.queryParentPlatByServerGBId(platformId);
+								Device device = deviceService.getDevice(platformId);
 
 							try {
 								if (platform != null) {
@@ -687,19 +557,16 @@ public class ZLMHttpHookListener {
 	 */
 	@ResponseBody
 	@PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
-	public JSONObject onStreamNoneReader(@RequestBody JSONObject json){
+	public JSONObject onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param){
 
-		logger.info("[ ZLM HOOK ]on_stream_none_reader API调用,参数:" + json.toString());
-		String mediaServerId = json.getString("mediaServerId");
-		String streamId = json.getString("stream");
-		String app = json.getString("app");
+		logger.info("[ZLM HOOK]流无人观看:{]->{}->{}/{}" + param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
 		// 录像下载
-		ret.put("close", userSetting.getStreamOnDemand());
-		if ("rtp".equals(app)){
+		if ("rtp".equals(param.getApp())){
+			ret.put("close", userSetting.getStreamOnDemand());
 			// 国标流, 点播/录像回放/录像下载
-			StreamInfo streamInfoForPlayCatch = redisCatchStorage.queryPlayByStreamId(streamId);
+			StreamInfo streamInfoForPlayCatch = redisCatchStorage.queryPlayByStreamId(param.getStream());
 			// 点播
 			if (streamInfoForPlayCatch != null) {
 				// 收到无人观看说明流也没有在往上级推送
@@ -718,7 +585,7 @@ public class ZLMHttpHookListener {
 						}
 					}
 				}
-				Device device = deviceService.queryDevice(streamInfoForPlayCatch.getDeviceID());
+				Device device = deviceService.getDevice(streamInfoForPlayCatch.getDeviceID());
 				if (device != null) {
 					try {
 						cmder.streamByeCmd(device, streamInfoForPlayCatch.getChannelId(),
@@ -733,12 +600,12 @@ public class ZLMHttpHookListener {
 				return ret;
 			}
 			// 录像回放
-			StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, streamId, null);
+			StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlayback(null, null, param.getStream(), null);
 			if (streamInfoForPlayBackCatch != null ) {
 				if (streamInfoForPlayBackCatch.isPause()) {
 					ret.put("close", false);
 				}else {
-					Device device = deviceService.queryDevice(streamInfoForPlayBackCatch.getDeviceID());
+					Device device = deviceService.getDevice(streamInfoForPlayBackCatch.getDeviceID());
 					if (device != null) {
 						try {
 							cmder.streamByeCmd(device,streamInfoForPlayBackCatch.getChannelId(),
@@ -754,7 +621,7 @@ public class ZLMHttpHookListener {
 				return ret;
 			}
 			// 录像下载
-			StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, streamId, null);
+			StreamInfo streamInfoForDownload = redisCatchStorage.queryDownload(null, null, param.getStream(), null);
 			// 进行录像下载时无人观看不断流
 			if (streamInfoForDownload != null) {
 				ret.put("close", false);
@@ -763,19 +630,19 @@ public class ZLMHttpHookListener {
 		}else {
 			// 非国标流 推流/拉流代理
 			// 拉流代理
-			StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(app, streamId);
+			StreamProxyItem streamProxyItem = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
 			if (streamProxyItem != null ) {
 				if (streamProxyItem.isEnable_remove_none_reader()) {
 					// 无人观看自动移除
 					ret.put("close", true);
-					streamProxyService.del(app, streamId);
+					streamProxyService.del(param.getApp(), param.getStream());
 					String url = streamProxyItem.getUrl() != null?streamProxyItem.getUrl():streamProxyItem.getSrc_url();
-					logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除",  app, streamId, url);
+					logger.info("[{}/{}]<-[{}] 拉流代理无人观看已经移除",  param.getApp(), param.getStream(), url);
 				}else if (streamProxyItem.isEnable_disable_none_reader()) {
 					// 无人观看停用
 					ret.put("close", true);
 					// 修改数据
-					streamProxyService.stop(app, streamId);
+					streamProxyService.stop(param.getApp(), param.getStream());
 				}else {
 					ret.put("close", false);
 				}
@@ -797,35 +664,33 @@ public class ZLMHttpHookListener {
 	 */
 	@ResponseBody
 	@PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
-	public JSONObject onStreamNotFound(@RequestBody JSONObject json){
-		if (logger.isDebugEnabled()) {
-			logger.debug("[ ZLM HOOK ]on_stream_not_found API调用,参数:" + json.toString());
-		}
-		String mediaServerId = json.getString("mediaServerId");
-		MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
-		if (userSetting.isAutoApplyPlay() && mediaInfo != null) {
-			String app = json.getString("app");
-			String streamId = json.getString("stream");
-			if ("rtp".equals(app)) {
-				if (mediaInfo.isRtpEnable()) {
-					String[] s = streamId.split("_");
-					if (s.length == 2) {
-						String deviceId = s[0];
-						String channelId = s[1];
-						Device device = redisCatchStorage.getDevice(deviceId);
-						if (device != null) {
-							playService.play(mediaInfo,deviceId, channelId, null, null, null);
+	public JSONObject onStreamNotFound(@RequestBody OnStreamNotFoundHookParam param){
+		logger.info("[ZLM HOOK] 流未找到:{}->{}->{}/{}" + param.getMediaServerId(), param.getSchema(), param.getApp(), param.getStream());
+		taskExecutor.execute(()->{
+			MediaServerItem mediaInfo = mediaServerService.getOne(param.getMediaServerId());
+			if (userSetting.isAutoApplyPlay() && mediaInfo != null) {
+				if ("rtp".equals(param.getApp())) {
+					if (mediaInfo.isRtpEnable()) {
+						String[] s = param.getStream().split("_");
+						if (s.length == 2) {
+							String deviceId = s[0];
+							String channelId = s[1];
+							Device device = redisCatchStorage.getDevice(deviceId);
+							if (device != null) {
+								playService.play(mediaInfo,deviceId, channelId, null, null, null);
+							}
 						}
 					}
-				}
-			}else {
-				// 拉流代理
-				StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(app, streamId);
-				if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnable_disable_none_reader()) {
-					streamProxyService.start(app, streamId);
+				}else {
+					// 拉流代理
+					StreamProxyItem streamProxyByAppAndStream = streamProxyService.getStreamProxyByAppAndStream(param.getApp(), param.getStream());
+					if (streamProxyByAppAndStream != null && streamProxyByAppAndStream.isEnable_disable_none_reader()) {
+						streamProxyService.start(param.getApp(), param.getStream());
+					}
 				}
 			}
-		}
+		});
+
 
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
@@ -841,22 +706,20 @@ public class ZLMHttpHookListener {
 	@PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
 	public JSONObject onServerStarted(HttpServletRequest request, @RequestBody JSONObject jsonObject){
 
-		if (logger.isDebugEnabled()) {
-			logger.debug("[ ZLM HOOK ]on_server_started API调用,参数:" + jsonObject.toString());
-		}
-		String remoteAddr = request.getRemoteAddr();
-		jsonObject.put("ip", remoteAddr);
-		List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
-		if (subscribes != null  && subscribes.size() > 0) {
-			for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
-				subscribe.response(null, jsonObject);
+		jsonObject.put("ip", request.getRemoteAddr());
+		ZLMServerConfig zlmServerConfig = JSON.to(ZLMServerConfig.class, jsonObject);
+		zlmServerConfig.setIp(request.getRemoteAddr());
+		logger.info("[ZLM HOOK] zlm 启动 " + zlmServerConfig.getGeneralMediaServerId());
+		taskExecutor.execute(()->{
+			List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_started);
+			if (subscribes != null  && subscribes.size() > 0) {
+				for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
+					subscribe.response(null, jsonObject);
+				}
 			}
-		}
-
-		ZLMServerConfig zlmServerConfig = JSONObject.toJavaObject(jsonObject, ZLMServerConfig.class);
-		if (zlmServerConfig !=null ) {
 			mediaServerService.zlmServerOnline(zlmServerConfig);
-		}
+		});
+
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
 		ret.put("msg", "success");
@@ -868,34 +731,59 @@ public class ZLMHttpHookListener {
 	 */
 	@ResponseBody
 	@PostMapping(value = "/on_send_rtp_stopped", produces = "application/json;charset=UTF-8")
-	public JSONObject onSendRtpStopped(HttpServletRequest request, @RequestBody JSONObject jsonObject){
+	public JSONObject onSendRtpStopped(HttpServletRequest request, @RequestBody OnSendRtpStoppedHookParam param){
 
-		logger.info("[ ZLM HOOK ]on_send_rtp_stopped API调用,参数:" + jsonObject);
+		logger.info("[ZLM HOOK] 发送rtp被动关闭:{}->{}/{}", param.getMediaServerId(), param.getApp(), param.getStream());
 
 		JSONObject ret = new JSONObject();
 		ret.put("code", 0);
 		ret.put("msg", "success");
 
 		// 查找对应的上级推流,发送停止
-		String app = jsonObject.getString("app");
-		if (!"rtp".equals(app)) {
+		if (!"rtp".equals(param.getApp())) {
 			return ret;
 		}
-		String stream = jsonObject.getString("stream");
-		List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(stream);
-		if (sendRtpItems.size() > 0) {
-			for (SendRtpItem sendRtpItem : sendRtpItems) {
-				ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
-				try {
-					commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
-				} catch (SipException | InvalidArgumentException | ParseException e) {
-					logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
+		taskExecutor.execute(()->{
+			List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServerByStream(param.getStream());
+			if (sendRtpItems.size() > 0) {
+				for (SendRtpItem sendRtpItem : sendRtpItems) {
+					ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(sendRtpItem.getPlatformId());
+					try {
+						commanderFroPlatform.streamByeCmd(parentPlatform, sendRtpItem.getCallId());
+					} catch (SipException | InvalidArgumentException | ParseException e) {
+						logger.error("[命令发送失败] 国标级联 发送BYE: {}", e.getMessage());
+					}
+					redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
+							sendRtpItem.getCallId(), sendRtpItem.getStreamId());
 				}
-				redisCatchStorage.deleteSendRTPServer(parentPlatform.getServerGBId(), sendRtpItem.getChannelId(),
-						sendRtpItem.getCallId(), sendRtpItem.getStreamId());
 			}
-		}
+		});
+
 
+		return ret;
+	}
+
+	/**
+	 * rtpServer收流超时
+	 */
+	@ResponseBody
+	@PostMapping(value = "/on_rtp_server_timeout", produces = "application/json;charset=UTF-8")
+	public JSONObject onRtpServerTimeout(HttpServletRequest request, @RequestBody OnRtpServerTimeoutHookParam param){
+		logger.info("[ZLM HOOK] rtpServer收流超时:{}->{}({})", param.getMediaServerId(), param.getStream_id(), param.getSsrc());
+
+		JSONObject ret = new JSONObject();
+		ret.put("code", 0);
+		ret.put("msg", "success");
+
+		taskExecutor.execute(()->{
+			JSONObject json = (JSONObject) JSON.toJSON(param);
+			List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_rtp_server_timeout);
+			if (subscribes != null  && subscribes.size() > 0) {
+				for (ZlmHttpHookSubscribe.Event subscribe : subscribes) {
+					subscribe.response(null, json);
+				}
+			}
+		});
 
 		return ret;
 	}

+ 7 - 6
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaListManager.java

@@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.media.zlm;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
 import com.genersoft.iot.vmp.media.zlm.dto.*;
+import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
 import com.genersoft.iot.vmp.service.IMediaServerService;
 import com.genersoft.iot.vmp.service.IStreamProxyService;
 import com.genersoft.iot.vmp.service.IStreamPushService;
@@ -67,19 +68,19 @@ public class ZLMMediaListManager {
 
     private Map<String, ChannelOnlineEvent> channelOnPublishEvents = new ConcurrentHashMap<>();
 
-    public StreamPushItem addPush(MediaItem mediaItem) {
-        StreamPushItem transform = streamPushService.transform(mediaItem);
-        StreamPushItem pushInDb = streamPushService.getPush(mediaItem.getApp(), mediaItem.getStream());
-        transform.setPushIng(mediaItem.isRegist());
+    public StreamPushItem addPush(OnStreamChangedHookParam onStreamChangedHookParam) {
+        StreamPushItem transform = streamPushService.transform(onStreamChangedHookParam);
+        StreamPushItem pushInDb = streamPushService.getPush(onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream());
+        transform.setPushIng(onStreamChangedHookParam.isRegist());
         transform.setUpdateTime(DateUtil.getNow());
         transform.setPushTime(DateUtil.getNow());
-        transform.setSelf(userSetting.getServerId().equals(mediaItem.getSeverId()));
+        transform.setSelf(userSetting.getServerId().equals(onStreamChangedHookParam.getSeverId()));
         if (pushInDb == null) {
             transform.setCreateTime(DateUtil.getNow());
             streamPushMapper.add(transform);
         }else {
             streamPushMapper.update(transform);
-            gbStreamMapper.updateMediaServer(mediaItem.getApp(), mediaItem.getStream(), mediaItem.getMediaServerId());
+            gbStreamMapper.updateMediaServer(onStreamChangedHookParam.getApp(), onStreamChangedHookParam.getStream(), onStreamChangedHookParam.getMediaServerId());
         }
         ChannelOnlineEvent channelOnlineEventLister = getChannelOnlineEventLister(transform.getApp(), transform.getStream());
         if ( channelOnlineEventLister != null)  {

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

@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.media.zlm;
 
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
 import okhttp3.*;
 import okhttp3.logging.HttpLoggingInterceptor;
@@ -169,7 +169,6 @@ public class ZLMRESTfulUtils {
                     .build();
             Response response = client.newCall(request).execute();
             if (response.isSuccessful()) {
-                logger.info("response body contentType: " + Objects.requireNonNull(response.body()).contentType());
                 if (targetPath != null) {
                     File snapFolder = new File(targetPath);
                     if (!snapFolder.exists()) {

+ 64 - 37
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java

@@ -1,17 +1,17 @@
 package com.genersoft.iot.vmp.media.zlm;
 
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.conf.UserSetting;
 import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaItem;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.genersoft.iot.vmp.media.zlm.dto.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
-import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
 
 import java.util.*;
 
@@ -26,6 +26,9 @@ public class ZLMRTPServerFactory {
     @Autowired
     private UserSetting userSetting;
 
+    @Autowired
+    private ZlmHttpHookSubscribe hookSubscribe;
+
     private int[] portRangeArray = new int[2];
 
     public int getFreePort(MediaServerItem mediaServerItem, int startPort, int endPort, List<Integer> usedFreelist) {
@@ -143,7 +146,7 @@ public class ZLMRTPServerFactory {
         return result;
     }
 
-    public boolean closeRTPServer(MediaServerItem serverItem, String streamId) {
+    public boolean closeRtpServer(MediaServerItem serverItem, String streamId) {
         boolean result = false;
         if (serverItem !=null){
             Map<String, Object> param = new HashMap<>();
@@ -163,6 +166,7 @@ public class ZLMRTPServerFactory {
         return result;
     }
 
+
     /**
      * 创建一个国标推流
      * @param ip 推流ip
@@ -175,21 +179,15 @@ public class ZLMRTPServerFactory {
      */
     public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp){
 
-        // 使用RTPServer 功能找一个可用的端口
-        String sendRtpPortRange = serverItem.getSendRtpPortRange();
-        if (ObjectUtils.isEmpty(sendRtpPortRange)) {
-            return null;
-        }
-        String[] portRangeStrArray = serverItem.getSendRtpPortRange().split(",");
-        int localPort = -1;
-        if (portRangeStrArray.length != 2) {
-            localPort = getFreePort(serverItem, 30000, 30500, null);
-        }else {
-            localPort = getFreePort(serverItem, Integer.parseInt(portRangeStrArray[0]),  Integer.parseInt(portRangeStrArray[1]), null);
-        }
-        if (localPort == -1) {
-            logger.error("没有可用的端口");
-            return null;
+        // 默认为随机端口
+        int localPort = 0;
+        if (userSetting.getGbSendStreamStrict()) {
+            if (userSetting.getGbSendStreamStrict()) {
+                localPort = keepPort(serverItem, ssrc);
+                if (localPort == 0) {
+                    return null;
+                }
+            }
         }
         SendRtpItem sendRtpItem = new SendRtpItem();
         sendRtpItem.setIp(ip);
@@ -217,21 +215,13 @@ public class ZLMRTPServerFactory {
      * @return SendRtpItem
      */
     public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp){
-        // 使用RTPServer 功能找一个可用的端口
-        String sendRtpPortRange = serverItem.getSendRtpPortRange();
-        if (ObjectUtils.isEmpty(sendRtpPortRange)) {
-            return null;
-        }
-        String[] portRangeStrArray = serverItem.getSendRtpPortRange().split(",");
-        int localPort = -1;
-        if (portRangeStrArray.length != 2) {
-            localPort = getFreePort(serverItem, 30000, 30500, null);
-        }else {
-            localPort = getFreePort(serverItem, Integer.parseInt(portRangeStrArray[0]),  Integer.parseInt(portRangeStrArray[1]), null);
-        }
-        if (localPort == -1) {
-            logger.error("没有可用的端口");
-            return null;
+        // 默认为随机端口
+        int localPort = 0;
+        if (userSetting.getGbSendStreamStrict()) {
+            localPort = keepPort(serverItem, ssrc);
+            if (localPort == 0) {
+                return null;
+            }
         }
         SendRtpItem sendRtpItem = new SendRtpItem();
         sendRtpItem.setIp(ip);
@@ -248,6 +238,42 @@ public class ZLMRTPServerFactory {
         return sendRtpItem;
     }
 
+    /**
+     * 保持端口,直到需要需要发流时再释放
+     */
+    public int keepPort(MediaServerItem serverItem, String ssrc) {
+        int localPort = 0;
+        Map<String, Object> param = new HashMap<>(3);
+        param.put("port", 0);
+        param.put("enable_tcp", 1);
+        param.put("stream_id", ssrc);
+        JSONObject jsonObject = zlmresTfulUtils.openRtpServer(serverItem, param);
+        if (jsonObject.getInteger("code") == 0) {
+            localPort = jsonObject.getInteger("port");
+            HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId());
+            // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
+            hookSubscribe.addSubscribe(hookSubscribeForRtpServerTimeout,
+                    (MediaServerItem mediaServerItem, JSONObject response)->{
+                        logger.info("[上级点播] {}->监听端口到期继续保持监听", ssrc);
+                        keepPort(serverItem, ssrc);
+                    });
+        }
+        logger.info("[上级点播] {}->监听端口: {}", ssrc, localPort);
+        return localPort;
+    }
+
+    /**
+     * 释放保持的端口
+     */
+    public boolean releasePort(MediaServerItem serverItem, String ssrc) {
+        logger.info("[上级点播] {}->释放监听端口", ssrc);
+        boolean closeRTPServerResult = closeRtpServer(serverItem, ssrc);
+        HookSubscribeForRtpServerTimeout hookSubscribeForRtpServerTimeout = HookSubscribeFactory.on_rtp_server_timeout(ssrc, null, serverItem.getId());
+        // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
+        hookSubscribe.removeSubscribe(hookSubscribeForRtpServerTimeout);
+        return closeRTPServerResult;
+    }
+
     /**
      * 调用zlm RESTFUL API —— startSendRtp
      */
@@ -275,7 +301,8 @@ public class ZLMRTPServerFactory {
      */
     public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) {
         JSONObject mediaInfo = zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId);
-        return (mediaInfo.getInteger("code") == 0
+        return mediaInfo != null && (mediaInfo.getInteger("code") == 0
+
                 && mediaInfo.getJSONArray("data") != null
                 && mediaInfo.getJSONArray("data").size() > 0);
     }
@@ -314,7 +341,7 @@ public class ZLMRTPServerFactory {
             result= true;
             logger.info("[停止RTP推流] 成功");
         } else {
-            logger.error("[停止RTP推流] 失败: {}, 参数:{}->\r\n{}",jsonObject.getString("msg"),JSONObject.toJSON(param), jsonObject);
+            logger.error("[停止RTP推流] 失败: {}, 参数:{}->\r\n{}",jsonObject.getString("msg"), JSON.toJSON(param), jsonObject);
         }
         return result;
     }

+ 21 - 11
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRunner.java

@@ -1,8 +1,8 @@
 package com.genersoft.iot.vmp.media.zlm;
 
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.conf.DynamicTask;
 import com.genersoft.iot.vmp.conf.MediaConfig;
 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
@@ -18,10 +18,14 @@ import org.springframework.core.annotation.Order;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 
-import java.util.*;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 @Component
-@Order(value=1)
+@Order(value=2)
 public class ZLMRunner implements CommandLineRunner {
 
     private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class);
@@ -62,7 +66,7 @@ public class ZLMRunner implements CommandLineRunner {
         // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
         hookSubscribe.addSubscribe(hookSubscribeForServerStarted,
                 (MediaServerItem mediaServerItem, JSONObject response)->{
-            ZLMServerConfig zlmServerConfig = JSONObject.toJavaObject(response, ZLMServerConfig.class);
+            ZLMServerConfig zlmServerConfig = response.to(ZLMServerConfig.class);
             if (zlmServerConfig !=null ) {
                 if (startGetMedia != null) {
                     startGetMedia.remove(zlmServerConfig.getGeneralMediaServerId());
@@ -73,34 +77,40 @@ public class ZLMRunner implements CommandLineRunner {
             }
         });
 
-
-
         // 获取zlm信息
         logger.info("[zlm] 等待默认zlm中...");
 
         // 获取所有的zlm, 并开启主动连接
         List<MediaServerItem> all = mediaServerService.getAllFromDatabase();
+        Map<String, MediaServerItem> allMap = new HashMap<>();
         mediaServerService.updateVmServer(all);
         if (all.size() == 0) {
             all.add(mediaConfig.getMediaSerItem());
         }
         for (MediaServerItem mediaServerItem : all) {
             if (startGetMedia == null) {
-                startGetMedia = new HashMap<>();
+                startGetMedia = new ConcurrentHashMap<>();
             }
             startGetMedia.put(mediaServerItem.getId(), true);
             connectZlmServer(mediaServerItem);
+            allMap.put(mediaServerItem.getId(), mediaServerItem);
         }
         String taskKey = "zlm-connect-timeout";
         dynamicTask.startDelay(taskKey, ()->{
-            if (startGetMedia != null) {
+            if (startGetMedia != null && startGetMedia.size() > 0) {
                 Set<String> allZlmId = startGetMedia.keySet();
                 for (String id : allZlmId) {
                     logger.error("[ {} ]]主动连接失败,不再尝试连接", id);
                 }
                 startGetMedia = null;
             }
-        //  TODO 清理数据库中与redis不匹配的zlm
+            // 获取redis中所有的zlm
+            List<MediaServerItem> allInRedis = mediaServerService.getAll();
+            for (MediaServerItem mediaServerItem : allInRedis) {
+                if (!allMap.containsKey(mediaServerItem.getId())) {
+                    mediaServerService.delete(mediaServerItem.getId());
+                }
+            }
         }, 60 * 1000 );
     }
 

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

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.media.zlm;
 
-import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson2.annotation.JSONField;
 
 public class ZLMServerConfig {
 
@@ -66,7 +66,7 @@ public class ZLMServerConfig {
     private String hookAdminParams;
 
     @JSONField(name = "hook.alive_interval")
-    private int hookAliveInterval;
+    private Float hookAliveInterval;
 
     @JSONField(name = "hook.enable")
     private String hookEnable;
@@ -798,11 +798,11 @@ public class ZLMServerConfig {
         this.shellPhell = shellPhell;
     }
 
-    public int getHookAliveInterval() {
+    public Float getHookAliveInterval() {
         return hookAliveInterval;
     }
 
-    public void setHookAliveInterval(int hookAliveInterval) {
+    public void setHookAliveInterval(Float hookAliveInterval) {
         this.hookAliveInterval = hookAliveInterval;
     }
 

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

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.media.zlm;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 import com.genersoft.iot.vmp.media.zlm.dto.HookType;
 import com.genersoft.iot.vmp.media.zlm.dto.IHookSubscribe;
 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;

+ 13 - 2
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeFactory.java

@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.media.zlm.dto;
 
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 
 /**
  * hook 订阅工厂
@@ -11,7 +11,7 @@ public class HookSubscribeFactory {
 
     public static HookSubscribeForStreamChange on_stream_changed(String app, String stream, boolean regist, String scheam, String mediaServerId) {
         HookSubscribeForStreamChange hookSubscribe = new HookSubscribeForStreamChange();
-        JSONObject subscribeKey = new com.alibaba.fastjson.JSONObject();
+        JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject();
         subscribeKey.put("app", app);
         subscribeKey.put("stream", stream);
         subscribeKey.put("regist", regist);
@@ -24,6 +24,17 @@ public class HookSubscribeFactory {
         return hookSubscribe;
     }
 
+    public static HookSubscribeForRtpServerTimeout on_rtp_server_timeout(String stream, String ssrc, String mediaServerId) {
+        HookSubscribeForRtpServerTimeout hookSubscribe = new HookSubscribeForRtpServerTimeout();
+        JSONObject subscribeKey = new com.alibaba.fastjson2.JSONObject();
+        subscribeKey.put("stream_id", stream);
+        subscribeKey.put("ssrc", ssrc);
+        subscribeKey.put("mediaServerId", mediaServerId);
+        hookSubscribe.setContent(subscribeKey);
+
+        return hookSubscribe;
+    }
+
     public static HookSubscribeForStreamPush on_publish(String app, String stream, String scheam, String mediaServerId) {
         HookSubscribeForStreamPush hookSubscribe = new HookSubscribeForStreamPush();
         JSONObject subscribeKey = new com.alibaba.fastjson.JSONObject();

+ 44 - 0
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForRtpServerTimeout.java

@@ -0,0 +1,44 @@
+package com.genersoft.iot.vmp.media.zlm.dto;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.alibaba.fastjson2.annotation.JSONField;
+
+import java.time.Instant;
+
+/**
+ * hook订阅-收流超时
+ * @author lin
+ */
+public class HookSubscribeForRtpServerTimeout implements IHookSubscribe{
+
+    private HookType hookType = HookType.on_rtp_server_timeout;
+
+    private JSONObject content;
+
+    @JSONField(format="yyyy-MM-dd HH:mm:ss")
+    private Instant expires;
+
+    @Override
+    public HookType getHookType() {
+        return hookType;
+    }
+
+    @Override
+    public JSONObject getContent() {
+        return content;
+    }
+
+    public void setContent(JSONObject content) {
+        this.content = content;
+    }
+
+    @Override
+    public Instant getExpires() {
+        return expires;
+    }
+
+    @Override
+    public void setExpires(Instant expires) {
+        this.expires = expires;
+    }
+}

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

@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.media.zlm.dto;
 
-import com.alibaba.fastjson.JSONObject;
-import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson2.JSONObject;
+import com.alibaba.fastjson2.annotation.JSONField;
 
 import java.time.Instant;
 

+ 3 - 2
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookSubscribeForStreamChange.java

@@ -1,7 +1,7 @@
 package com.genersoft.iot.vmp.media.zlm.dto;
 
-import com.alibaba.fastjson.JSONObject;
-import com.alibaba.fastjson.annotation.JSONField;
+import com.alibaba.fastjson2.JSONObject;
+import com.alibaba.fastjson2.annotation.JSONField;
 
 import java.time.Instant;
 
@@ -15,6 +15,7 @@ public class HookSubscribeForStreamChange implements IHookSubscribe{
 
     private JSONObject content;
 
+    @JSONField(format="yyyy-MM-dd HH:mm:ss")
     private Instant expires;
 
     @Override

+ 2 - 0
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookType.java

@@ -19,5 +19,7 @@ public enum HookType {
     on_stream_none_reader,
     on_stream_not_found,
     on_server_started,
+
+    on_rtp_server_timeout,
     on_server_keepalive
 }

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/IHookSubscribe.java

@@ -1,6 +1,6 @@
 package com.genersoft.iot.vmp.media.zlm.dto;
 
-import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson2.JSONObject;
 
 import java.time.Instant;
 

+ 3 - 16
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaServerItem.java

@@ -5,7 +5,6 @@ import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
 import io.swagger.v3.oas.annotations.media.Schema;
 import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
 
 import java.util.HashMap;
 
@@ -55,7 +54,7 @@ public class MediaServerItem{
     private String secret;
 
     @Schema(description = "keepalive hook触发间隔,单位秒")
-    private int hookAliveInterval;
+    private Float hookAliveInterval;
 
     @Schema(description = "是否使用多端口模式")
     private boolean rtpEnable;
@@ -66,9 +65,6 @@ public class MediaServerItem{
     @Schema(description = "多端口RTP收流端口范围")
     private String rtpPortRange;
 
-    @Schema(description = "RTP发流端口范围")
-    private String sendRtpPortRange;
-
     @Schema(description = "assist服务端口")
     private int recordAssistPort;
 
@@ -119,7 +115,6 @@ public class MediaServerItem{
         hookAliveInterval = zlmServerConfig.getHookAliveInterval();
         rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口
         rtpPortRange = zlmServerConfig.getPortRange().replace("_",","); // 默认使用30000,30500作为级联时发送流的端口号
-        sendRtpPortRange = "30000,30500"; // 默认使用30000,30500作为级联时发送流的端口号
         recordAssistPort = 0; // 默认关闭
 
     }
@@ -324,19 +319,11 @@ public class MediaServerItem{
         this.lastKeepaliveTime = lastKeepaliveTime;
     }
 
-    public String getSendRtpPortRange() {
-        return sendRtpPortRange;
-    }
-
-    public void setSendRtpPortRange(String sendRtpPortRange) {
-        this.sendRtpPortRange = sendRtpPortRange;
-    }
-
-    public int getHookAliveInterval() {
+    public Float getHookAliveInterval() {
         return hookAliveInterval;
     }
 
-    public void setHookAliveInterval(int hookAliveInterval) {
+    public void setHookAliveInterval(Float hookAliveInterval) {
         this.hookAliveInterval = hookAliveInterval;
     }
 }

+ 4 - 0
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/ServerKeepaliveData.java

@@ -0,0 +1,4 @@
+package com.genersoft.iot.vmp.media.zlm.dto;
+
+public class ServerKeepaliveData {
+}

+ 9 - 6
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamAuthorityInfo.java

@@ -1,5 +1,8 @@
 package com.genersoft.iot.vmp.media.zlm.dto;
 
+import com.genersoft.iot.vmp.media.zlm.dto.hook.OnPublishHookParam;
+import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
+
 /**
  * 流的鉴权信息
  * @author lin
@@ -102,13 +105,13 @@ public class StreamAuthorityInfo {
         return streamAuthorityInfo;
     }
 
-    public static StreamAuthorityInfo getInstanceByHook(MediaItem mediaItem) {
+    public static StreamAuthorityInfo getInstanceByHook(OnStreamChangedHookParam onStreamChangedHookParam) {
         StreamAuthorityInfo streamAuthorityInfo = new StreamAuthorityInfo();
-        streamAuthorityInfo.setApp(mediaItem.getApp());
-        streamAuthorityInfo.setStream(mediaItem.getStream());
-        streamAuthorityInfo.setId(mediaItem.getMediaServerId());
-        streamAuthorityInfo.setOriginType(mediaItem.getOriginType());
-        streamAuthorityInfo.setOriginTypeStr(mediaItem.getOriginTypeStr());
+        streamAuthorityInfo.setApp(onStreamChangedHookParam.getApp());
+        streamAuthorityInfo.setStream(onStreamChangedHookParam.getStream());
+        streamAuthorityInfo.setId(onStreamChangedHookParam.getMediaServerId());
+        streamAuthorityInfo.setOriginType(onStreamChangedHookParam.getOriginType());
+        streamAuthorityInfo.setOriginTypeStr(onStreamChangedHookParam.getOriginTypeStr());
         return streamAuthorityInfo;
     }
 }

+ 7 - 7
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/StreamPushItem.java

@@ -1,10 +1,10 @@
 package com.genersoft.iot.vmp.media.zlm.dto;
 
 import com.genersoft.iot.vmp.gb28181.bean.GbStream;
+import com.genersoft.iot.vmp.media.zlm.dto.hook.OnStreamChangedHookParam;
 import com.genersoft.iot.vmp.utils.DateUtil;
 import io.swagger.v3.oas.annotations.media.Schema;
 import org.jetbrains.annotations.NotNull;
-import org.springframework.util.unit.DataUnit;
 
 import java.util.List;
 
@@ -59,7 +59,7 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
      * 客户端和服务器网络信息,可能为null类型
      */
     @Schema(description = "客户端和服务器网络信息,可能为null类型")
-    private MediaItem.OriginSock originSock;
+    private OnStreamChangedHookParam.OriginSock originSock;
 
     /**
      * 产生源类型的字符串描述
@@ -83,7 +83,7 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
      * 音视频轨道
      */
     @Schema(description = "音视频轨道")
-    private List<MediaItem.MediaTrack> tracks;
+    private List<OnStreamChangedHookParam.MediaTrack> tracks;
 
     /**
      * 音视频轨道
@@ -223,11 +223,11 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
         this.originType = originType;
     }
 
-    public MediaItem.OriginSock getOriginSock() {
+    public OnStreamChangedHookParam.OriginSock getOriginSock() {
         return originSock;
     }
 
-    public void setOriginSock(MediaItem.OriginSock originSock) {
+    public void setOriginSock(OnStreamChangedHookParam.OriginSock originSock) {
         this.originSock = originSock;
     }
 
@@ -256,11 +256,11 @@ public class StreamPushItem extends GbStream implements Comparable<StreamPushIte
         this.aliveSecond = aliveSecond;
     }
 
-    public List<MediaItem.MediaTrack> getTracks() {
+    public List<OnStreamChangedHookParam.MediaTrack> getTracks() {
         return tracks;
     }
 
-    public void setTracks(List<MediaItem.MediaTrack> tracks) {
+    public void setTracks(List<OnStreamChangedHookParam.MediaTrack> tracks) {
         this.tracks = tracks;
     }
 

+ 1 - 1
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/HookParam.java

@@ -1,4 +1,4 @@
-package com.genersoft.iot.vmp.media.zlm.dto;
+package com.genersoft.iot.vmp.media.zlm.dto.hook;
 
 /**
  * zlm hook事件的参数

+ 5 - 1
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/OnPlayHookParam.java

@@ -1,4 +1,4 @@
-package com.genersoft.iot.vmp.media.zlm.dto;
+package com.genersoft.iot.vmp.media.zlm.dto.hook;
 
 /**
  * zlm hook事件中的on_play事件的参数
@@ -79,4 +79,8 @@ public class OnPlayHookParam extends HookParam{
         this.vhost = vhost;
     }
 
+    @Override
+    public String toString() {
+        return String.format("%s://%s:%s/%s/%s?%s", schema, ip, port, app, stream, params);
+    }
 }

+ 5 - 1
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/OnPublishHookParam.java

@@ -1,4 +1,4 @@
-package com.genersoft.iot.vmp.media.zlm.dto;
+package com.genersoft.iot.vmp.media.zlm.dto.hook;
 
 /**
  * zlm hook事件中的on_publish事件的参数
@@ -79,4 +79,8 @@ public class OnPublishHookParam extends HookParam{
         this.vhost = vhost;
     }
 
+    @Override
+    public String toString() {
+        return String.format("%s://%s:%s/%s/%s?%s", schema, ip, port, app, stream, params);
+    }
 }

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

@@ -0,0 +1,53 @@
+package com.genersoft.iot.vmp.media.zlm.dto.hook;
+
+/**
+ * zlm hook事件中的on_rtp_server_timeout事件的参数
+ * @author lin
+ */
+public class OnRtpServerTimeoutHookParam extends HookParam{
+    private int local_port;
+    private String stream_id;
+    private int tcpMode;
+    private boolean re_use_port;
+    private String ssrc;
+
+    public int getLocal_port() {
+        return local_port;
+    }
+
+    public void setLocal_port(int local_port) {
+        this.local_port = local_port;
+    }
+
+    public String getStream_id() {
+        return stream_id;
+    }
+
+    public void setStream_id(String stream_id) {
+        this.stream_id = stream_id;
+    }
+
+    public int getTcpMode() {
+        return tcpMode;
+    }
+
+    public void setTcpMode(int tcpMode) {
+        this.tcpMode = tcpMode;
+    }
+
+    public boolean isRe_use_port() {
+        return re_use_port;
+    }
+
+    public void setRe_use_port(boolean re_use_port) {
+        this.re_use_port = re_use_port;
+    }
+
+    public String getSsrc() {
+        return ssrc;
+    }
+
+    public void setSsrc(String ssrc) {
+        this.ssrc = ssrc;
+    }
+}

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

@@ -0,0 +1,27 @@
+package com.genersoft.iot.vmp.media.zlm.dto.hook;
+
+/**
+ * zlm hook事件中的on_send_rtp_stopped事件的参数
+ * @author lin
+ */
+public class OnSendRtpStoppedHookParam extends HookParam{
+    private String app;
+    private String stream;
+
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getStream() {
+        return stream;
+    }
+
+    public void setStream(String stream) {
+        this.stream = stream;
+    }
+}

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

@@ -0,0 +1,20 @@
+package com.genersoft.iot.vmp.media.zlm.dto.hook;
+
+import com.genersoft.iot.vmp.media.zlm.dto.ServerKeepaliveData;
+
+/**
+ * zlm hook事件中的on_play事件的参数
+ * @author lin
+ */
+public class OnServerKeepaliveHookParam extends HookParam{
+
+    private ServerKeepaliveData data;
+
+    public ServerKeepaliveData getData() {
+        return data;
+    }
+
+    public void setData(ServerKeepaliveData data) {
+        this.data = data;
+    }
+}

+ 0 - 0
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/MediaItem.java


Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác