Forráskód Böngészése

摄像头服务代码整合

hecheng 3 éve
szülő
commit
e3c8dc4900
100 módosított fájl, 14980 hozzáadás és 26 törlés
  1. 17 0
      zd-common/zd-common-redis/src/main/java/com/zd/common/redis/configure/RedisConfig.java
  2. 5 1
      zd-common/zd-common-security/src/main/java/com/zd/common/security/annotation/EnableCustomConfig.java
  3. 46 1
      zd-modules/zd-algorithm/pom.xml
  4. 19 2
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/AlgorithmApplication.java
  5. 7 6
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/alarm/utils/AlarmUtil.java
  6. 15 16
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/face/handle/FaceEngineFactory.java
  7. 172 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/common/ApiSaveConstant.java
  8. 177 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/common/StreamInfo.java
  9. 136 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/common/VersionPo.java
  10. 54 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/common/VideoManagerConstants.java
  11. 196 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/MediaConfig.java
  12. 240 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/ProxyServletConfig.java
  13. 91 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/RedisConfig.java
  14. 109 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/SipConfig.java
  15. 36 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/SipDeviceRunner.java
  16. 64 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/SipPlatformRunner.java
  17. 117 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/Swagger3Config.java
  18. 107 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/UserSetup.java
  19. 25 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/VManagerConfig.java
  20. 37 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/VersionConfig.java
  21. 37 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/VersionInfo.java
  22. 24 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/WVPTimerTask.java
  23. 256 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/SipLayer.java
  24. 295 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java
  25. 31 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/auth/RegisterLogicHandler.java
  26. 24 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/BaiduPoint.java
  27. 273 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/Device.java
  28. 124 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/DeviceAlarm.java
  29. 432 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/DeviceChannel.java
  30. 89 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/GbStream.java
  31. 35 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/Host.java
  32. 166 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/MobilePosition.java
  33. 267 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/ParentPlatform.java
  34. 56 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/ParentPlatformCatch.java
  35. 32 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/PlatformGbStream.java
  36. 15 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/PlatformRegister.java
  37. 55 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/RecordInfo.java
  38. 122 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/RecordItem.java
  39. 177 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/SendRtpItem.java
  40. 68 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/SsrcTransaction.java
  41. 149 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/WvpSipDate.java
  42. 24 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/DeviceOffLineDetector.java
  43. 70 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/EventPublisher.java
  44. 42 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/SipSubscribe.java
  45. 31 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/alarm/AlarmEvent.java
  46. 58 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/alarm/AlarmEventListener.java
  47. 61 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/offline/KeepaliveTimeoutListenerForPlatform.java
  48. 48 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/offline/KeepliveTimeoutListener.java
  49. 40 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/offline/OfflineEvent.java
  50. 66 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/offline/OfflineEventListener.java
  51. 42 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/online/OnlineEvent.java
  52. 85 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/online/OnlineEventListener.java
  53. 28 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEvent.java
  54. 87 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEventLister.java
  55. 25 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEvent.java
  56. 105 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java
  57. 140 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/session/SsrcConfig.java
  58. 106 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/session/VideoStreamSessionManager.java
  59. 230 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/SIPProcessorFactory.java
  60. 78 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java
  61. 65 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java
  62. 51 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/callback/RequestMessage.java
  63. 302 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
  64. 64 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
  65. 226 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
  66. 210 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java
  67. 1517 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
  68. 322 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
  69. 12 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/ISIPRequestProcessor.java
  70. 131 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/SIPRequestAbstractProcessor.java
  71. 142 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java
  72. 150 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java
  73. 28 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/CancelRequestProcessor.java
  74. 478 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java
  75. 1083 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
  76. 394 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/NotifyRequestProcessor.java
  77. 31 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java
  78. 193 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java
  79. 62 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/SubscribeRequestProcessor.java
  80. 19 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/response/ISIPResponseProcessor.java
  81. 30 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/response/impl/ByeResponseProcessor.java
  82. 32 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/response/impl/CancelResponseProcessor.java
  83. 74 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java
  84. 31 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/response/impl/OtherResponseProcessor.java
  85. 100 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/response/impl/RegisterResponseProcessor.java
  86. 57 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/utils/DateUtil.java
  87. 46 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/utils/NumericUtil.java
  88. 164 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/utils/XmlUtil.java
  89. 436 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/ZLMHttpHookListener.java
  90. 117 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java
  91. 154 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/ZLMMediaListManager.java
  92. 254 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/ZLMRESTfulUtils.java
  93. 256 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/ZLMRTPServerFactory.java
  94. 166 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/ZLMRunner.java
  95. 794 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/ZLMServerConfig.java
  96. 379 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/dto/MediaItem.java
  97. 282 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/dto/MediaServerItem.java
  98. 145 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/dto/StreamProxyItem.java
  99. 220 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/dto/StreamPushItem.java
  100. 0 0
      zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/dto/ZLMRunInfo.java

+ 17 - 0
zd-common/zd-common-redis/src/main/java/com/zd/common/redis/configure/RedisConfig.java

@@ -11,6 +11,7 @@ import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.data.redis.connection.RedisConnectionFactory;
 import org.springframework.data.redis.connection.RedisConnectionFactory;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 
 
 /**
 /**
@@ -21,6 +22,7 @@ import org.springframework.data.redis.serializer.StringRedisSerializer;
 @Configuration
 @Configuration
 @EnableCaching
 @EnableCaching
 public class RedisConfig extends CachingConfigurerSupport {
 public class RedisConfig extends CachingConfigurerSupport {
+
     @Bean
     @Bean
     @SuppressWarnings(value = {"unchecked", "rawtypes"})
     @SuppressWarnings(value = {"unchecked", "rawtypes"})
     public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
     public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
@@ -45,4 +47,19 @@ public class RedisConfig extends CachingConfigurerSupport {
         template.afterPropertiesSet();
         template.afterPropertiesSet();
         return template;
         return template;
     }
     }
+
+    /**
+     * redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器
+     * 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理
+     *
+     * @param connectionFactory
+     * @return
+     */
+    @Bean
+    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
+
+        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
+        container.setConnectionFactory(connectionFactory);
+        return container;
+    }
 }
 }

+ 5 - 1
zd-common/zd-common-security/src/main/java/com/zd/common/security/annotation/EnableCustomConfig.java

@@ -4,6 +4,7 @@ import com.zd.common.core.properties.SnowflakeProperties;
 import com.zd.common.security.config.ApplicationConfig;
 import com.zd.common.security.config.ApplicationConfig;
 import com.zd.common.security.feign.FeignAutoConfiguration;
 import com.zd.common.security.feign.FeignAutoConfiguration;
 import org.mybatis.spring.annotation.MapperScan;
 import org.mybatis.spring.annotation.MapperScan;
+import org.mybatis.spring.annotation.MapperScans;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
 import org.springframework.context.annotation.Import;
 import org.springframework.context.annotation.Import;
@@ -21,7 +22,10 @@ import java.lang.annotation.*;
 // 表示通过aop框架暴露该代理对象,AopContext能够访问
 // 表示通过aop框架暴露该代理对象,AopContext能够访问
 @EnableAspectJAutoProxy(exposeProxy = true)
 @EnableAspectJAutoProxy(exposeProxy = true)
 // 指定要扫描的Mapper类的包的路径
 // 指定要扫描的Mapper类的包的路径
-@MapperScan("com.zd.**.mapper")
+@MapperScans({
+        @MapperScan("com.zd.**.mapper"),
+        @MapperScan("com.zd.**.dao")
+})
 // 开启线程异步执行
 // 开启线程异步执行
 @EnableAsync
 @EnableAsync
 // 自动加载类
 // 自动加载类

+ 46 - 1
zd-modules/zd-algorithm/pom.xml

@@ -10,7 +10,6 @@
     <modelVersion>4.0.0</modelVersion>
     <modelVersion>4.0.0</modelVersion>
     <groupId>com.zd.alg</groupId>
     <groupId>com.zd.alg</groupId>
     <artifactId>zd-algorithm</artifactId>
     <artifactId>zd-algorithm</artifactId>
-    <version>1.0</version>
     <description>算法服务</description>
     <description>算法服务</description>
 
 
     <dependencies>
     <dependencies>
@@ -116,5 +115,51 @@
             <artifactId>spring-mock</artifactId>
             <artifactId>spring-mock</artifactId>
             <version>2.0.8</version>
             <version>2.0.8</version>
         </dependency>
         </dependency>
+
+        <dependency>
+            <groupId>connect</groupId>
+            <artifactId>connect-lib</artifactId>
+            <version>1.0.0</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/src/main/resources/libs/connect-lib.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>reader</groupId>
+            <artifactId>reader-lib</artifactId>
+            <version>1.0.0</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/src/main/resources/libs/reader-lib.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>com.gg.reader</groupId>
+            <artifactId>greader-api</artifactId>
+            <version>1.0.0</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/src/main/resources/libs/reader.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>com.zd</groupId>
+            <artifactId>zd-bottle-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <!--反向代理-->
+        <dependency>
+            <groupId>org.mitre.dsmiley.httpproxy</groupId>
+            <artifactId>smiley-http-proxy-servlet</artifactId>
+            <version>1.12</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.sip</groupId>
+            <artifactId>jain-sip-ri</artifactId>
+            <version>1.3.0-91</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.dom4j</groupId>
+            <artifactId>dom4j</artifactId>
+            <version>2.1.3</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
     </dependencies>
 </project>
 </project>

+ 19 - 2
zd-modules/zd-algorithm/src/main/java/com/zd/alg/AlgorithmApplication.java

@@ -3,23 +3,40 @@ package com.zd.alg;
 
 
 import com.zd.alg.forward.config.AlgorithmYml;
 import com.zd.alg.forward.config.AlgorithmYml;
 import com.zd.alg.forward.properties.FireProperties;
 import com.zd.alg.forward.properties.FireProperties;
+import com.zd.common.core.constant.Constants;
 import com.zd.common.security.annotation.EnableCustomConfig;
 import com.zd.common.security.annotation.EnableCustomConfig;
 import com.zd.common.security.annotation.EnableRyFeignClients;
 import com.zd.common.security.annotation.EnableRyFeignClients;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
+import org.mybatis.spring.annotation.MapperScan;
+import org.mybatis.spring.annotation.MapperScans;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.servlet.ServletComponentScan;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.ConfigurableApplicationContext;
 import zd.common.mqtt.config.MqttProperties;
 import zd.common.mqtt.config.MqttProperties;
 
 
+@ServletComponentScan("com.zd.alg.iot.vmp.conf")
+@EnableFeignClients(basePackages = Constants.BASE_PACKAGE)
 @EnableCustomConfig
 @EnableCustomConfig
 @EnableRyFeignClients
 @EnableRyFeignClients
-@SpringBootApplication
+@SpringBootApplication(scanBasePackages = Constants.BASE_PACKAGE)
 @EnableConfigurationProperties({MqttProperties.class, AlgorithmYml.class, FireProperties.class})
 @EnableConfigurationProperties({MqttProperties.class, AlgorithmYml.class, FireProperties.class})
 @Slf4j
 @Slf4j
 public class AlgorithmApplication {
 public class AlgorithmApplication {
 
 
+    private static String[] args;
+    private static ConfigurableApplicationContext context;
     public static void main(String[] args) {
     public static void main(String[] args) {
-        SpringApplication.run(AlgorithmApplication.class,args);
+        AlgorithmApplication.args=args;
+        context = SpringApplication.run(AlgorithmApplication.class, args);
+    }
+
+    // 项目重启
+    public static void restart() {
+        context.close();
+        AlgorithmApplication.context = SpringApplication.run(AlgorithmApplication.class, args);
     }
     }
 }
 }

+ 7 - 6
zd-modules/zd-algorithm/src/main/java/com/zd/alg/alarm/utils/AlarmUtil.java

@@ -14,6 +14,7 @@ import org.apache.commons.collections4.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
@@ -31,11 +32,11 @@ import java.util.*;
 public class AlarmUtil {
 public class AlarmUtil {
     private static final Logger logger = LoggerFactory.getLogger(AlarmUtil.class);
     private static final Logger logger = LoggerFactory.getLogger(AlarmUtil.class);
 
 
-//    @Value("${alarm.host}")
-    private String alarmUrl = "192.168.1.100" ;
+    @Value("${alarm.host}")
+    private String alarmUrl ;
 
 
-//    @Value("${alarm.retry}")
-    private Integer retryCount = 3 ;
+    @Value("${alarm.retry}")
+    private Integer retryCount ;
 
 
     @Autowired
     @Autowired
     private IAlarmLogService alarmLogService;
     private IAlarmLogService alarmLogService;
@@ -46,8 +47,8 @@ public class AlarmUtil {
      */
      */
     @PostConstruct
     @PostConstruct
     private void alarmInint(){
     private void alarmInint(){
-        sendGet(Routes.SystemStatusSnapshot);
-        sendPost(new AlarmEntrty(Routes.NoticePush, new String[]{"18681887295"}, SendTypes.Call+"","启动项目电话报警服务测试!"));
+//        sendGet(Routes.SystemStatusSnapshot);
+//        sendPost(new AlarmEntrty(Routes.NoticePush, new String[]{"18681887295"}, SendTypes.Call+"","启动项目电话报警服务测试!"));
     }
     }
 
 
     /**
     /**

+ 15 - 16
zd-modules/zd-algorithm/src/main/java/com/zd/alg/face/handle/FaceEngineFactory.java

@@ -57,6 +57,7 @@ public class FaceEngineFactory {
 
 
     /**
     /**
      * 获取激活信息
      * 获取激活信息
+     *
      * @param faceEngine
      * @param faceEngine
      * @return
      * @return
      */
      */
@@ -72,10 +73,9 @@ public class FaceEngineFactory {
 
 
     public String getPath() throws IOException {
     public String getPath() throws IOException {
         FileSystemResource resource = new FileSystemResource("../");
         FileSystemResource resource = new FileSystemResource("../");
-        String canonicalPath  = resource.getFile().getCanonicalPath();
-        if(StrUtil.isNotBlank(faceProperties.getLib_folder()))
-        {
-            canonicalPath =canonicalPath+File.separator+faceProperties.getLib_folder();
+        String canonicalPath = resource.getFile().getCanonicalPath();
+        if (StrUtil.isNotBlank(faceProperties.getLib_folder())) {
+            canonicalPath = canonicalPath + File.separator + faceProperties.getLib_folder();
         }
         }
         return canonicalPath;
         return canonicalPath;
     }
     }
@@ -83,13 +83,12 @@ public class FaceEngineFactory {
     public void activation(FaceEngine faceEngine) {
     public void activation(FaceEngine faceEngine) {
 
 
         //
         //
-        String key ="";
-        String os =  System.getProperty("os.name");
+        String key = "";
+        String os = System.getProperty("os.name");
         if (os.toLowerCase().startsWith("win")) {
         if (os.toLowerCase().startsWith("win")) {
-            key=faceProperties.getWinKey();
-        }else
-        {
-            key=faceProperties.getLinuxKey();
+            key = faceProperties.getWinKey();
+        } else {
+            key = faceProperties.getLinuxKey();
 
 
         }
         }
         logger.info("配置信息打印:" + faceProperties.getAppId() + ", " + key);
         logger.info("配置信息打印:" + faceProperties.getAppId() + ", " + key);
@@ -103,8 +102,7 @@ public class FaceEngineFactory {
     /**
     /**
      * 在线激活
      * 在线激活
      */
      */
-    public void activeOnline()
-    {
+    public void activeOnline() {
         FaceEngine faceEngine = getFaceEngine();
         FaceEngine faceEngine = getFaceEngine();
         if (faceEngine == null) return;
         if (faceEngine == null) return;
         activation(faceEngine);
         activation(faceEngine);
@@ -112,7 +110,7 @@ public class FaceEngineFactory {
     }
     }
 
 
     private FaceEngine getFaceEngine() {
     private FaceEngine getFaceEngine() {
-        FaceEngine faceEngine =null;
+        FaceEngine faceEngine = null;
         //默认放在资源文件下
         //默认放在资源文件下
 
 
         String canonicalPath = null;
         String canonicalPath = null;
@@ -121,10 +119,10 @@ public class FaceEngineFactory {
             // 里面用的System.load方法,没办法通过路径加载jar文件中的文件
             // 里面用的System.load方法,没办法通过路径加载jar文件中的文件
             //所以这里只能把lib文件放在jar文件外部
             //所以这里只能把lib文件放在jar文件外部
             canonicalPath = getPath();
             canonicalPath = getPath();
-            logger.info("获取lib文件路径!:{}",canonicalPath);
-            faceEngine =new FaceEngine(canonicalPath);
+            logger.info("获取lib文件路径!:{}", canonicalPath);
+            faceEngine = new FaceEngine(canonicalPath);
         } catch (IOException e) {
         } catch (IOException e) {
-            logger.error("lib文件路径获取失败,引擎初始化中断,请正确配置后激活!当前读取路径:{}",canonicalPath);
+            logger.error("lib文件路径获取失败,引擎初始化中断,请正确配置后激活!当前读取路径:{}", canonicalPath);
             throw new ServiceException("lib文件加载失败!");
             throw new ServiceException("lib文件加载失败!");
         }
         }
         return faceEngine;
         return faceEngine;
@@ -133,6 +131,7 @@ public class FaceEngineFactory {
 
 
     /**
     /**
      * 注入IMAGE 模式配置
      * 注入IMAGE 模式配置
+     *
      * @return
      * @return
      */
      */
     @Bean
     @Bean

+ 172 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/common/ApiSaveConstant.java

@@ -0,0 +1,172 @@
+package com.zd.alg.iot.vmp.common;
+
+public class ApiSaveConstant {
+
+    public static String getVal(String key) {
+        String[] keyItemArray = key.split("/");
+        if (keyItemArray.length <= 1 || !"api".equals(keyItemArray[1])) {
+            return null;
+        }
+        if (keyItemArray.length >= 4) {
+            switch (keyItemArray[2]) {
+                case "alarm":
+                    if ("delete".equals(keyItemArray[3])) {
+                        return "删除报警";
+                    }
+                    break;
+                case "device":
+                    switch (keyItemArray[3]) {
+                        case "config":
+                            if (keyItemArray.length >= 5 && "basicParam".equals(keyItemArray[4])) {
+                                return "[设备配置] 基本配置设置命令";
+                            }
+                            break;
+                        case "control":
+                            switch (keyItemArray[4]) {
+                                case "teleboot":
+                                    return "[设备控制] 远程启动";
+                                case "record":
+                                    return "[设备控制] 录像控制";
+                                case "guard":
+                                    return "[设备控制] 布防/撤防命令";
+                                case "reset_alarm":
+                                    return "[设备控制] 报警复位";
+                                case "i_frame":
+                                    return "[设备控制] 强制关键帧";
+                                case "home_position":
+                                    return "[设备控制] 看守位控制";
+                            }
+                            break;
+                            case "query":
+                                if (keyItemArray.length <= 5) return null;
+                                switch (keyItemArray[4]) {
+                                    case "devices":
+                                        if (keyItemArray.length < 7) return null;
+                                        switch (keyItemArray[6]) {
+                                            case "sync":
+                                                return "[设备查询] 同步设备通道";
+                                            case "delete":
+                                                return "[设备查询] 移除设备";
+                                        }
+                                        break;
+                                    case "channel":
+                                        return "[设备查询] 更新通道信息";
+                                    case "transport":
+                                        return "[设备查询] 修改数据流传输模式";
+                                }
+                                break;
+                            }
+                case "gbStream":
+                    switch (keyItemArray[3]) {
+                        case "del":
+                            return "移除通道与国标的关联";
+                        case "add":
+                            return "添加通道与国标的关联";
+                    }
+                    break;
+                case "media":
+                    break;
+                case "position":
+                    if ("subscribe".equals(keyItemArray[3])) {
+                        return "订阅位置信息";
+                    }
+                    break;
+                case "platform":
+                    switch (keyItemArray[3]) {
+                        case "save":
+                            return "添加上级平台";
+                        case "delete":
+                            return "移除上级平台";
+                        case "update_channel_for_gb":
+                            return "向上级平台添加国标通道";
+                        case "del_channel_for_gb":
+                            return "从上级平台移除国标通道";
+                    }
+                    break;
+                case "platform_gb_stream":
+                    break;
+                case "play":
+                    switch (keyItemArray[3]) {
+                        case "start":
+                            return "开始点播";
+                        case "stop":
+                            return "停止点播";
+                        case "convert":
+                            return "转码";
+                        case "convertStop":
+                            return "结束转码";
+                        case "broadcast":
+                            return "语音广播";
+                    }
+                    break;
+                case "download":
+                    switch (keyItemArray[3]) {
+                        case "start":
+                            return "开始历史媒体下载";
+                        case "stop":
+                            return "停止历史媒体下载";
+                    }
+                    break;
+                case "playback":
+                    switch (keyItemArray[3]) {
+                        case "start":
+                            return "开始视频回放";
+                        case "stop":
+                            return "停止视频回放";
+                    }
+                    break;
+                case "ptz":
+                    switch (keyItemArray[3]) {
+                        case "control":
+                            return "云台控制";
+                        case "front_end_command":
+                            return "通用前端控制命令";
+                    }
+                    break;
+                case "gb_record":
+                    break;
+                case "onvif":
+                    break;
+                case "server":
+                    if ("restart".equals(keyItemArray[3])) {
+                        return "重启流媒体服务";
+                    }
+                    break;
+                case "proxy":
+                    switch (keyItemArray[3]) {
+                        case "save":
+                            return "保存代理";
+                        case "del":
+                            return "移除代理";
+                        case "start":
+                            return "启用代理";
+                        case "stop":
+                            return "停用代理";
+                    }
+                    break;
+                case "push":
+                    switch (keyItemArray[3]) {
+                        case "save_to_gb":
+                            return "将推流添加到国标";
+                        case "remove_form_gb":
+                            return "将推流移出到国标";
+                    }
+                    break;
+                case "user":
+                    switch (keyItemArray[3]) {
+                        case "login":
+                            return "登录";
+                        case "changePassword":
+                            return "修改密码";
+                        case "add":
+                            return "添加用户";
+                        case "delete":
+                            return "删除用户";
+                    }
+                    break;
+            }
+        }
+        return null;
+    }
+}
+

+ 177 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/common/StreamInfo.java

@@ -0,0 +1,177 @@
+package com.zd.alg.iot.vmp.common;
+
+import com.alibaba.fastjson.JSONArray;
+
+public class StreamInfo {
+
+    private String app;
+    private String streamId;
+    private String deviceID;
+    private String channelId;
+    private String flv;
+    private String ws_flv;
+    private String fmp4;
+    private String ws_fmp4;
+    private String hls;
+    private String ws_hls;
+    private String ts;
+    private String ws_ts;
+    private String rtmp;
+    private String rtsp;
+    private String rtc;
+    private String mediaServerId;
+    private JSONArray tracks;
+
+    public static class TransactionInfo{
+        public String callId;
+        public String localTag;
+        public String remoteTag;
+        public String branch;
+    }
+
+    private TransactionInfo transactionInfo;
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getDeviceID() {
+        return deviceID;
+    }
+
+    public void setDeviceID(String deviceID) {
+        this.deviceID = deviceID;
+    }
+
+    public String getChannelId() {
+        return channelId;
+    }
+
+    public void setChannelId(String channelId) {
+        this.channelId = channelId;
+    }
+
+    public String getFlv() {
+        return flv;
+    }
+
+    public void setFlv(String flv) {
+        this.flv = flv;
+    }
+
+    public String getWs_flv() {
+        return ws_flv;
+    }
+
+    public void setWs_flv(String ws_flv) {
+        this.ws_flv = ws_flv;
+    }
+
+    public String getRtmp() {
+        return rtmp;
+    }
+
+    public void setRtmp(String rtmp) {
+        this.rtmp = rtmp;
+    }
+
+    public String getHls() {
+        return hls;
+    }
+
+    public void setHls(String hls) {
+        this.hls = hls;
+    }
+
+    public String getRtsp() {
+        return rtsp;
+    }
+
+    public void setRtsp(String rtsp) {
+        this.rtsp = rtsp;
+    }
+
+    public JSONArray getTracks() {
+        return tracks;
+    }
+
+    public void setTracks(JSONArray tracks) {
+        this.tracks = tracks;
+    }
+
+    public String getFmp4() {
+        return fmp4;
+    }
+
+    public void setFmp4(String fmp4) {
+        this.fmp4 = fmp4;
+    }
+
+    public String getWs_fmp4() {
+        return ws_fmp4;
+    }
+
+    public void setWs_fmp4(String ws_fmp4) {
+        this.ws_fmp4 = ws_fmp4;
+    }
+
+    public String getWs_hls() {
+        return ws_hls;
+    }
+
+    public void setWs_hls(String ws_hls) {
+        this.ws_hls = ws_hls;
+    }
+
+    public String getTs() {
+        return ts;
+    }
+
+    public void setTs(String ts) {
+        this.ts = ts;
+    }
+
+    public String getWs_ts() {
+        return ws_ts;
+    }
+
+    public void setWs_ts(String ws_ts) {
+        this.ws_ts = ws_ts;
+    }
+
+    public String getStreamId() {
+        return streamId;
+    }
+
+    public void setStreamId(String streamId) {
+        this.streamId = streamId;
+    }
+
+    public String getRtc() {
+        return rtc;
+    }
+
+    public void setRtc(String rtc) {
+        this.rtc = rtc;
+    }
+
+    public TransactionInfo getTransactionInfo() {
+        return transactionInfo;
+    }
+
+    public void setTransactionInfo(TransactionInfo transactionInfo) {
+        this.transactionInfo = transactionInfo;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+}

+ 136 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/common/VersionPo.java

@@ -0,0 +1,136 @@
+package com.zd.alg.iot.vmp.common;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+public class VersionPo {
+    /**
+     * git的全版本号
+     */
+    @JSONField(name="GIT-Revision")
+    private String GIT_Revision;
+    /**
+     * maven版本
+     */
+    @JSONField(name = "Create-By")
+    private String Create_By;
+    /**
+     * git的分支
+     */
+    @JSONField(name = "GIT-BRANCH")
+    private String GIT_BRANCH;
+    /**
+     * git的url
+     */
+    @JSONField(name = "GIT-URL")
+    private String GIT_URL;
+    /**
+     * 构建日期
+     */
+    @JSONField(name = "BUILD-DATE")
+    private String BUILD_DATE;
+    /**
+     * 项目名称 配合pom使用
+     */
+    @JSONField(name = "artifactId")
+    private String artifactId;
+    /**
+     * git局部版本号
+     */
+    @JSONField(name = "GIT-Revision-SHORT")
+    private String GIT_Revision_SHORT;
+    /**
+     * 项目的版本如2.0.1.0 配合pom使用
+     */
+    @JSONField(name = "version")
+    private String version;
+    /**
+     * 子系统名称
+     */
+    @JSONField(name = "project")
+    private String project;
+    /**
+     * jdk版本
+     */
+    @JSONField(name="Build_Jdk")
+    private String Build_Jdk;
+
+    public void setGIT_Revision(String GIT_Revision) {
+        this.GIT_Revision = GIT_Revision;
+    }
+
+    public void setCreate_By(String create_By) {
+        Create_By = create_By;
+    }
+
+    public void setGIT_BRANCH(String GIT_BRANCH) {
+        this.GIT_BRANCH = GIT_BRANCH;
+    }
+
+    public void setGIT_URL(String GIT_URL) {
+        this.GIT_URL = GIT_URL;
+    }
+
+    public void setBUILD_DATE(String BUILD_DATE) {
+        this.BUILD_DATE = BUILD_DATE;
+    }
+
+    public void setArtifactId(String artifactId) {
+        this.artifactId = artifactId;
+    }
+
+    public void setGIT_Revision_SHORT(String GIT_Revision_SHORT) {
+        this.GIT_Revision_SHORT = GIT_Revision_SHORT;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public void setProject(String project) {
+        this.project = project;
+    }
+
+    public void setBuild_Jdk(String build_Jdk) {
+        Build_Jdk = build_Jdk;
+    }
+
+    public String getGIT_Revision() {
+        return GIT_Revision;
+    }
+
+    public String getCreate_By() {
+        return Create_By;
+    }
+
+    public String getGIT_BRANCH() {
+        return GIT_BRANCH;
+    }
+
+    public String getGIT_URL() {
+        return GIT_URL;
+    }
+
+    public String getBUILD_DATE() {
+        return BUILD_DATE;
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public String getGIT_Revision_SHORT() {
+        return GIT_Revision_SHORT;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public String getProject() {
+        return project;
+    }
+
+    public String getBuild_Jdk() {
+        return Build_Jdk;
+    }
+}

+ 54 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/common/VideoManagerConstants.java

@@ -0,0 +1,54 @@
+package com.zd.alg.iot.vmp.common;
+
+/**
+ * @Description: 定义常量
+ * @author: dgs
+ * @date:   2019年5月30日 下午3:04:04
+ *
+ */
+public class VideoManagerConstants {
+
+	public static final String WVP_SERVER_PREFIX = "VMP_wvp_server";
+
+	public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_";
+
+	public static final String MEDIA_SERVERS_ONLINE_PREFIX = "VMP_MEDIA_ONLINE_SERVERS";
+
+	public static final String MEDIA_STREAM_PREFIX = "VMP_MEDIA_STREAM";
+
+	public static final String DEVICE_PREFIX = "VMP_DEVICE_";
+
+	public static final String CACHEKEY_PREFIX = "VMP_CHANNEL_";
+
+	public static final String KEEPLIVEKEY_PREFIX = "VMP_keeplive_";
+
+	public static final String PLAYER_PREFIX = "VMP_PLAYER_";
+
+	public static final String PLAY_BLACK_PREFIX = "VMP_PLAYBACK_";
+
+	public static final String PLATFORM_KEEPLIVEKEY_PREFIX = "VMP_PLATFORM_KEEPLIVE_";
+
+	public static final String PLATFORM_CATCH_PREFIX = "VMP_PLATFORM_CATCH_";
+
+	public static final String PLATFORM_REGISTER_PREFIX = "VMP_PLATFORM_REGISTER_";
+
+	public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_PLATFORM_REGISTER_INFO_";
+
+	public static final String PLATFORM_SEND_RTP_INFO_PREFIX = "VMP_PLATFORM_SEND_RTP_INFO_";
+
+	public static final String Pattern_Topic = "VMP_KEEPLIVE_PLATFORM_";
+
+	public static final String EVENT_ONLINE_REGISTER = "1";
+
+	public static final String EVENT_ONLINE_KEEPLIVE = "2";
+
+	public static final String EVENT_ONLINE_MESSAGE = "3";
+
+	public static final String EVENT_OUTLINE_UNREGISTER = "1";
+
+	public static final String EVENT_OUTLINE_TIMEOUT = "2";
+
+	public static final String MEDIA_SSRC_USED_PREFIX = "VMP_media_used_ssrc_";
+
+	public static final String MEDIA_TRANSACTION_USED_PREFIX = "VMP_media_transaction_";
+}

+ 196 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/MediaConfig.java

@@ -0,0 +1,196 @@
+package com.zd.alg.iot.vmp.conf;
+
+import com.zd.alg.iot.vmp.media.zlm.dto.MediaServerItem;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.StringUtils;
+
+import java.text.SimpleDateFormat;
+
+@Configuration("mediaConfig")
+public class MediaConfig{
+
+    @Value("${media.id:}")
+    private String id;
+
+    @Value("${media.ip}")
+    private String ip;
+
+    @Value("${media.hook-ip:${sip.ip}}")
+    private String hookIp;
+
+    @Value("${sip.ip}")
+    private String sipIp;
+
+    @Value("${sip.domain}")
+    private String sipDomain;
+
+    @Value("${media.sdp-ip:${media.ip}}")
+    private String sdpIp;
+
+    @Value("${media.stream-ip:${media.ip}}")
+    private String streamIp;
+
+    @Value("${media.http-port}")
+    private Integer httpPort;
+
+    @Value("${media.http-ssl-port:0}")
+    private Integer httpSSlPort = 0;
+
+    @Value("${media.rtmp-port:0}")
+    private Integer rtmpPort = 0;
+
+    @Value("${media.rtmp-ssl-port:0}")
+    private Integer rtmpSSlPort = 0;
+
+    @Value("${media.rtp-proxy-port:0}")
+    private Integer rtpProxyPort = 0;
+
+    @Value("${media.rtsp-port:0}")
+    private Integer rtspPort = 0;
+
+    @Value("${media.rtsp-ssl-port:0}")
+    private Integer rtspSSLPort = 0;
+
+    @Value("${media.auto-config:true}")
+    private boolean autoConfig = true;
+
+    @Value("${media.secret}")
+    private String secret;
+
+    @Value("${media.stream-none-reader-delay-ms:18000}")
+    private String streamNoneReaderDelayMS = "18000";
+
+    @Value("${media.rtp.enable}")
+    private boolean rtpEnable;
+
+    @Value("${media.rtp.port-range}")
+    private String rtpPortRange;
+
+    @Value("${media.record-assist-port:0}")
+    private Integer recordAssistPort = 0;
+
+    public String getId() {
+        return id;
+    }
+
+    public String getIp() {
+        return ip;
+    }
+
+    public String getHookIp() {
+        if (StringUtils.isEmpty(hookIp)){
+            return sipIp;
+        }else {
+            return hookIp;
+        }
+
+    }
+
+    public String getSipIp() {
+        return sipIp;
+    }
+
+    public int getHttpPort() {
+        return httpPort;
+    }
+
+    public int getHttpSSlPort() {
+        return httpSSlPort;
+    }
+
+    public int getRtmpPort() {
+        return rtmpPort;
+    }
+
+    public int getRtmpSSlPort() {
+        return rtmpSSlPort;
+    }
+
+    public int getRtpProxyPort() {
+        if (rtpProxyPort == null) {
+            return 0;
+        }else {
+            return rtpProxyPort;
+        }
+
+    }
+
+    public int getRtspPort() {
+        return rtspPort;
+    }
+
+    public int getRtspSSLPort() {
+        return rtspSSLPort;
+    }
+
+    public boolean isAutoConfig() {
+        return autoConfig;
+    }
+
+    public String getSecret() {
+        return secret;
+    }
+
+    public String getStreamNoneReaderDelayMS() {
+        return streamNoneReaderDelayMS;
+    }
+
+    public boolean isRtpEnable() {
+        return rtpEnable;
+    }
+
+    public String getRtpPortRange() {
+        return rtpPortRange;
+    }
+
+    public int getRecordAssistPort() {
+        return recordAssistPort;
+    }
+
+    public String getSdpIp() {
+        if (StringUtils.isEmpty(sdpIp)){
+            return ip;
+        }else {
+            return sdpIp;
+        }
+    }
+
+    public String getStreamIp() {
+        if (StringUtils.isEmpty(streamIp)){
+            return ip;
+        }else {
+            return streamIp;
+        }
+    }
+
+    public MediaServerItem getMediaSerItem(){
+        MediaServerItem mediaServerItem = new MediaServerItem();
+        mediaServerItem.setId(id);
+        mediaServerItem.setIp(ip);
+        mediaServerItem.setDefaultServer(true);
+        mediaServerItem.setHookIp(getHookIp());
+        mediaServerItem.setSdpIp(sdpIp);
+        mediaServerItem.setStreamIp(streamIp);
+        mediaServerItem.setHttpPort(httpPort);
+        mediaServerItem.setHttpSSlPort(httpSSlPort);
+        mediaServerItem.setRtmpPort(rtmpPort);
+        mediaServerItem.setRtmpSSlPort(rtmpSSlPort);
+        mediaServerItem.setRtpProxyPort(rtpProxyPort);
+        mediaServerItem.setRtspPort(rtspPort);
+        mediaServerItem.setRtspSSLPort(rtspSSLPort);
+        mediaServerItem.setAutoConfig(autoConfig);
+        mediaServerItem.setSecret(secret);
+        mediaServerItem.setStreamNoneReaderDelayMS(streamNoneReaderDelayMS);
+        mediaServerItem.setRtpEnable(rtpEnable);
+        mediaServerItem.setRtpPortRange(rtpPortRange);
+        mediaServerItem.setRecordAssistPort(recordAssistPort);
+
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        mediaServerItem.setCreateTime(format.format(System.currentTimeMillis()));
+        mediaServerItem.setUpdateTime(format.format(System.currentTimeMillis()));
+
+        return mediaServerItem;
+    }
+
+}

+ 240 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/ProxyServletConfig.java

@@ -0,0 +1,240 @@
+package com.zd.alg.iot.vmp.conf;
+
+import com.zd.alg.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.zd.alg.iot.vmp.service.IMediaServerService;
+import org.apache.catalina.connector.ClientAbortException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.mitre.dsmiley.httpproxy.ProxyServlet;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.servlet.ServletRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.net.ConnectException;
+
+@SuppressWarnings(value = {"rawtypes", "unchecked"})
+@Configuration
+public class ProxyServletConfig {
+
+    private final static Logger logger = LoggerFactory.getLogger(ProxyServletConfig.class);
+
+    @Autowired
+    private IMediaServerService mediaServerService;
+
+    @Value("${server.port}")
+    private int serverPort;
+
+    @Bean
+    public ServletRegistrationBean zlmServletRegistrationBean(){
+        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new ZLMProxySerlet(),"/zlm/*");
+        servletRegistrationBean.setName("zlm_Proxy");
+        servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:6080");
+        servletRegistrationBean.addUrlMappings();
+        if (logger.isDebugEnabled()) {
+            servletRegistrationBean.addInitParameter("log", "true");
+        }
+        return servletRegistrationBean;
+    }
+
+    class ZLMProxySerlet extends ProxyServlet{
+        @Override
+        protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
+            String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
+            MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
+            if (mediaInfo != null) {
+                if (!StringUtils.isEmpty(queryStr)) {
+                    queryStr += "&secret=" + mediaInfo.getSecret();
+                }else {
+                    queryStr = "secret=" + mediaInfo.getSecret();
+                }
+            }
+            return queryStr;
+        }
+
+        /**
+         * 异常处理
+         */
+        @Override
+        protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){
+            try {
+                super.handleRequestException(proxyRequest, proxyResonse, e);
+            } catch (ServletException servletException) {
+                logger.error("zlm 代理失败: ", e);
+            } catch (IOException ioException) {
+                if (ioException instanceof ConnectException) {
+                    logger.error("zlm 连接失败");
+                } else if (ioException instanceof ClientAbortException) {
+                    logger.error("zlm: 用户已中断连接,代理终止");
+                } else {
+                    logger.error("zlm 代理失败: ", e);
+                }
+            } catch (RuntimeException exception){
+                logger.error("zlm 代理失败: ", e);
+            }
+        }
+
+        /**
+         * 对于为按照格式请求的可以直接返回404
+         */
+        @Override
+        protected String getTargetUri(HttpServletRequest servletRequest) {
+            String requestURI = servletRequest.getRequestURI();
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+
+            String uri = null;
+            if (mediaInfo != null) {
+//                String realRequestURI = requestURI.substring(requestURI.indexOf(mediaInfo.getId())+ mediaInfo.getId().length());
+                uri = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort());
+            }else {
+                uri = "http://127.0.0.1:" + serverPort +"/index/hook/null"; // 只是一个能返回404的请求而已, 其他的也可以
+            }
+            return uri;
+        }
+
+        /**
+         * 动态替换请求目标
+         */
+        @Override
+        protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
+            String requestURI = servletRequest.getRequestURI();
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            HttpHost host;
+            if (mediaInfo != null) {
+                host = new HttpHost(mediaInfo.getIp(), mediaInfo.getHttpPort());
+            }else {
+                host = new HttpHost("127.0.0.1", serverPort);
+            }
+            return host;
+
+        }
+
+        /**
+         * 根据uri获取流媒体信息
+         */
+        MediaServerItem getMediaInfoByUri(String uri){
+            String[] split = uri.split("/");
+            String mediaServerId = split[2];
+            return mediaServerService.getOne(mediaServerId);
+        }
+
+        /**
+         * 去掉url中的标志信息
+         */
+        @Override
+        protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) {
+            String requestURI = servletRequest.getRequestURI();
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            String url = super.rewriteUrlFromRequest(servletRequest);
+            if (mediaInfo == null) {
+                return  url;
+            }
+            return url.replace(mediaInfo.getId() + "/", "");
+        }
+    }
+
+    @Bean
+    public ServletRegistrationBean recordServletRegistrationBean(){
+        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new RecordProxySerlet(),"/record_proxy/*");
+        servletRegistrationBean.setName("record_proxy");
+        servletRegistrationBean.addInitParameter("targetUri", "http://127.0.0.1:18081");
+        servletRegistrationBean.addUrlMappings();
+        if (logger.isDebugEnabled()) {
+            servletRegistrationBean.addInitParameter("log", "true");
+        }
+        return servletRegistrationBean;
+    }
+
+    class RecordProxySerlet extends ProxyServlet{
+
+
+        /**
+         * 异常处理
+         */
+        @Override
+        protected void handleRequestException(HttpRequest proxyRequest, HttpResponse proxyResonse, Exception e){
+            try {
+                super.handleRequestException(proxyRequest, proxyResonse, e);
+            } catch (ServletException servletException) {
+                logger.error("录像服务 代理失败: ", e);
+            } catch (IOException ioException) {
+                if (ioException instanceof ConnectException) {
+                    logger.error("录像服务 连接失败");
+                } else if (ioException instanceof ClientAbortException) {
+                    logger.error("录像服务:用户已中断连接,代理终止");
+                } else {
+                    logger.error("录像服务 代理失败: ", e);
+                }
+            } catch (RuntimeException exception){
+                logger.error("录像服务 代理失败: ", e);
+            }
+        }
+
+        /**
+         * 对于为按照格式请求的可以直接返回404
+         */
+        @Override
+        protected String getTargetUri(HttpServletRequest servletRequest) {
+            String requestURI = servletRequest.getRequestURI();
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+
+            String uri = null;
+            if (mediaInfo != null) {
+//                String realRequestURI = requestURI.substring(requestURI.indexOf(mediaInfo.getId())+ mediaInfo.getId().length());
+                uri = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getRecordAssistPort());
+            }else {
+                uri = "http://127.0.0.1:" + serverPort +"/index/hook/null"; // 只是一个能返回404的请求而已, 其他的也可以
+            }
+            return uri;
+        }
+
+        /**
+         * 动态替换请求目标
+         */
+        @Override
+        protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
+            String requestURI = servletRequest.getRequestURI();
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            HttpHost host;
+            if (mediaInfo != null) {
+                host = new HttpHost(mediaInfo.getIp(), mediaInfo.getRecordAssistPort());
+            }else {
+                host = new HttpHost("127.0.0.1", serverPort);
+            }
+            return host;
+
+        }
+
+        /**
+         * 根据uri获取流媒体信息
+         */
+        MediaServerItem getMediaInfoByUri(String uri){
+            String[] split = uri.split("/");
+            String mediaServerId = split[2];
+            return mediaServerService.getOne(mediaServerId);
+        }
+
+        /**
+         * 去掉url中的标志信息
+         */
+        @Override
+        protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) {
+            String requestURI = servletRequest.getRequestURI();
+            MediaServerItem mediaInfo = getMediaInfoByUri(requestURI);
+            String url = super.rewriteUrlFromRequest(servletRequest);
+            if (mediaInfo == null) {
+                return  url;
+            }
+            return url.replace(mediaInfo.getId() + "/", "");
+        }
+    }
+
+}

+ 91 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/RedisConfig.java

@@ -0,0 +1,91 @@
+//package com.zd.alg.iot.vmp.conf;
+//
+//import org.apache.commons.lang3.StringUtils;
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.cache.annotation.CachingConfigurerSupport;
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//import org.springframework.data.redis.connection.RedisConnectionFactory;
+//import org.springframework.data.redis.core.RedisTemplate;
+//import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+//import org.springframework.data.redis.serializer.StringRedisSerializer;
+//
+//import com.alibaba.fastjson.parser.ParserConfig;
+//import com.zd.alg.iot.vmp.utils.redis.FastJsonRedisSerializer;
+//import redis.clients.jedis.JedisPool;
+//import redis.clients.jedis.JedisPoolConfig;
+//
+///**
+// * @Description:Redis中间件配置类,使用spring-data-redis集成,自动从application.yml中加载redis配置
+// * @author: dgs
+// * @date: 2019年5月30日 上午10:58:25
+// *
+// */
+//@Configuration
+//public class RedisConfig extends CachingConfigurerSupport {
+//
+//	@Value("${spring.redis.host}")
+//	private String host;
+//	@Value("${spring.redis.port}")
+//	private int port;
+//	@Value("${spring.redis.database}")
+//	private int database;
+//	@Value("${spring.redis.password}")
+//	private String password;
+//	@Value("${spring.redis.timeout:2000}")
+//	private int timeout;
+//	@Value("${spring.redis.poolMaxTotal:1000}")
+//	private int poolMaxTotal;
+//	@Value("${spring.redis.poolMaxIdle:500}")
+//	private int poolMaxIdle;
+//	@Value("${spring.redis.poolMaxWait:5}")
+//	private int poolMaxWait;
+//
+//	@Bean
+//	public JedisPool jedisPool() {
+//		if (StringUtils.isBlank(password)) {
+//			password = null;
+//		}
+//		JedisPoolConfig poolConfig = new JedisPoolConfig();
+//		poolConfig.setMaxIdle(poolMaxIdle);
+//		poolConfig.setMaxTotal(poolMaxTotal);
+//		// 秒转毫秒
+//		poolConfig.setMaxWaitMillis(poolMaxWait * 1000L);
+//		JedisPool jp = new JedisPool(poolConfig, host, port, timeout * 1000, password, database);
+//		return jp;
+//	}
+//
+//	@Bean("redisTemplate")
+//	public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
+//		RedisTemplate<Object, Object> template = new RedisTemplate<>();
+//		template.setConnectionFactory(redisConnectionFactory);
+//		// 使用fastjson进行序列化处理,提高解析效率
+//		FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<Object>(Object.class);
+//		// value值的序列化采用fastJsonRedisSerializer
+//		template.setValueSerializer(serializer);
+//		template.setHashValueSerializer(serializer);
+//		// key的序列化采用StringRedisSerializer
+//		template.setKeySerializer(new StringRedisSerializer());
+//		template.setHashKeySerializer(new StringRedisSerializer());
+//		template.setConnectionFactory(redisConnectionFactory);
+//		// 使用fastjson时需设置此项,否则会报异常not support type
+//		ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
+//		return template;
+//	}
+//
+//	/**
+//	 * redis消息监听器容器 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器
+//	 * 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理
+//	 *
+//	 * @param connectionFactory
+//	 * @return
+//	 */
+//	@Bean
+//	RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) {
+//
+//        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
+//        container.setConnectionFactory(connectionFactory);
+//        return container;
+//    }
+//
+//}

+ 109 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/SipConfig.java

@@ -0,0 +1,109 @@
+package com.zd.alg.iot.vmp.conf;
+
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true)
+public class SipConfig {
+
+	private String ip;
+
+	/**
+	 * 默认使用 0.0.0.0
+	 */
+	private String monitorIp = "0.0.0.0";
+
+	private Integer port;
+
+	private String domain;
+
+	private String id;
+
+	private String password;
+
+	Integer ptzSpeed = 50;
+
+	Integer keepaliveTimeOut = 180;
+
+	Integer registerTimeInterval = 30000;
+
+	public void setIp(String ip) {
+		this.ip = ip;
+	}
+
+	public void setMonitorIp(String monitorIp) {
+		this.monitorIp = monitorIp;
+	}
+
+	public void setPort(Integer port) {
+		this.port = port;
+	}
+
+	public void setDomain(String domain) {
+		this.domain = domain;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	public void setPtzSpeed(Integer ptzSpeed) {
+		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;
+	}
+
+
+	public Integer getPort() {
+		return port;
+	}
+
+
+	public String getDomain() {
+		return domain;
+	}
+
+
+	public String getId() {
+		return id;
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+
+	public Integer getPtzSpeed() {
+		return ptzSpeed;
+	}
+
+	public Integer getKeepaliveTimeOut() {
+		return keepaliveTimeOut;
+	}
+
+	public Integer getRegisterTimeInterval() {
+		return registerTimeInterval;
+	}
+}

+ 36 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/SipDeviceRunner.java

@@ -0,0 +1,36 @@
+package com.zd.alg.iot.vmp.conf;
+
+import com.zd.alg.iot.vmp.storager.IRedisCatchStorage;
+import com.zd.alg.iot.vmp.storager.IVideoManagerStorager;
+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.List;
+
+
+/**
+ * 系统启动时控制设备离线
+ */
+@Component
+@Order(value=4)
+public class SipDeviceRunner implements CommandLineRunner {
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Override
+    public void run(String... args) throws Exception {
+        // 读取redis没有心跳信息的则设置为离线,等收到下次心跳设置为在线
+        // 设置所有设备离线
+        storager.outlineForAll();
+        List<String> onlineForAll = redisCatchStorage.getOnlineForAll();
+        for (String deviceId : onlineForAll) {
+            storager.online(deviceId);
+        }
+    }
+}

+ 64 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/SipPlatformRunner.java

@@ -0,0 +1,64 @@
+package com.zd.alg.iot.vmp.conf;
+
+import com.zd.alg.iot.vmp.gb28181.bean.ParentPlatform;
+import com.zd.alg.iot.vmp.gb28181.bean.ParentPlatformCatch;
+import com.zd.alg.iot.vmp.gb28181.event.EventPublisher;
+import com.zd.alg.iot.vmp.media.zlm.ZLMRTPServerFactory;
+import com.zd.alg.iot.vmp.storager.IRedisCatchStorage;
+import com.zd.alg.iot.vmp.storager.IVideoManagerStorager;
+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.List;
+
+/**
+ * 系统启动时控制上级平台重新注册
+ */
+@Component
+@Order(value=3)
+public class SipPlatformRunner implements CommandLineRunner {
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private EventPublisher publisher;
+
+    @Autowired
+    private ZLMRTPServerFactory zlmrtpServerFactory;
+
+
+    @Override
+    public void run(String... args) throws Exception {
+        // 设置所有平台离线
+        storager.outlineForAllParentPlatform();
+
+        // 清理所有平台注册缓存
+        redisCatchStorage.cleanPlatformRegisterInfos();
+
+        // 停止所有推流
+//        zlmrtpServerFactory.closeAllSendRtpStream();
+
+        List<ParentPlatform> parentPlatforms = storager.queryEnableParentPlatformList(true);
+
+        for (ParentPlatform parentPlatform : parentPlatforms) {
+            redisCatchStorage.updatePlatformRegister(parentPlatform);
+
+            redisCatchStorage.updatePlatformKeepalive(parentPlatform);
+
+            ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch();
+
+            parentPlatformCatch.setParentPlatform(parentPlatform);
+            parentPlatformCatch.setId(parentPlatform.getServerGBId());
+            redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
+
+            // 发送平台未注册消息
+            publisher.platformNotRegisterEventPublish(parentPlatform.getServerGBId());
+        }
+    }
+}

+ 117 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/Swagger3Config.java

@@ -0,0 +1,117 @@
+//package com.zd.alg.iot.vmp.conf;
+//
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//import springfox.documentation.builders.ApiInfoBuilder;
+//import springfox.documentation.builders.PathSelectors;
+//import springfox.documentation.builders.RequestHandlerSelectors;
+//import springfox.documentation.service.ApiInfo;
+//import springfox.documentation.service.Contact;
+//import springfox.documentation.spi.DocumentationType;
+//import springfox.documentation.spring.web.plugins.Docket;
+//
+//@Configuration
+//public class Swagger3Config {
+//
+//    @Value("${swagger-ui.enabled}")
+//    private boolean enable;
+//
+//    @Bean
+//    public Docket createRestApi() {
+//        return new Docket(DocumentationType.OAS_30)
+//                .apiInfo(apiInfo())
+//                .groupName("1. 全部")
+//                .select()
+//                .apis(RequestHandlerSelectors.basePackage("com.zd.iot.vmp.vmanager"))
+//                .paths(PathSelectors.any())
+//                .build()
+//                .pathMapping("/")
+//                .enable(enable);
+//    }
+//    @Bean
+//    public Docket createRestGBApi() {
+//        return new Docket(DocumentationType.OAS_30)
+//                .apiInfo(apiInfo())
+//                .groupName("2. 国标28181")
+//                .select()
+//                .apis(RequestHandlerSelectors.basePackage("com.zd.iot.vmp.vmanager.gb28181"))
+//                .paths(PathSelectors.any())
+//                .build()
+//                .pathMapping("/")
+//                .enable(enable);
+//    }
+//
+//    @Bean
+//    public Docket createRestONVIFApi() {
+//        return new Docket(DocumentationType.OAS_30)
+//                .apiInfo(apiInfo())
+//                .groupName("3. ONVIF")
+//                .select()
+//                .apis(RequestHandlerSelectors.basePackage("com.zd.iot.vmp.vmanager.onvif"))
+//                .paths(PathSelectors.any())
+//                .build()
+//                .pathMapping("/")
+//                .enable(enable);
+//    }
+//
+//    @Bean
+//    public Docket createRestStreamProxyApi() {
+//        return new Docket(DocumentationType.OAS_30)
+//                .apiInfo(apiInfo())
+//                .groupName("4. 拉流转发")
+//                .select()
+//                .apis(RequestHandlerSelectors.basePackage("com.zd.iot.vmp.vmanager.streamProxy"))
+//                .paths(PathSelectors.any())
+//                .build()
+//                .pathMapping("/")
+//                .enable(enable);
+//    }
+//    @Bean
+//    public Docket createRestStreamPushApi() {
+//        return new Docket(DocumentationType.OAS_30)
+//                .apiInfo(apiInfo())
+//                .groupName("5. 推流管理")
+//                .select()
+//                .apis(RequestHandlerSelectors.basePackage("com.zd.iot.vmp.vmanager.streamPush"))
+//                .paths(PathSelectors.any())
+//                .build()
+//                .pathMapping("/")
+//                .enable(enable);
+//    }
+//
+//
+//    @Bean
+//    public Docket createServerApi() {
+//        return new Docket(DocumentationType.OAS_30)
+//                .apiInfo(apiInfo())
+//                .groupName("6. 服务管理")
+//                .select()
+//                .apis(RequestHandlerSelectors.basePackage("com.zd.iot.vmp.vmanager.server"))
+//                .paths(PathSelectors.any())
+//                .build()
+//                .pathMapping("/")
+//                .enable(enable);
+//    }
+//    @Bean
+//    public Docket createUserApi() {
+//        return new Docket(DocumentationType.OAS_30)
+//                .apiInfo(apiInfo())
+//                .groupName("7. 用户管理")
+//                .select()
+//                .apis(RequestHandlerSelectors.basePackage("com.zd.iot.vmp.vmanager.user"))
+//                .paths(PathSelectors.any())
+//                .build()
+//                .pathMapping("/")
+//                .enable(enable);
+//    }
+//
+//    private ApiInfo apiInfo() {
+//        return new ApiInfoBuilder()
+//                .title("WVP-PRO 接口文档")
+//                .description("更多请咨询服务开发者(18010473990@@163.com)。")
+//                .contact(new Contact("panlinlin", "http://www.ruiyeclub.cn", "ruiyeclub@foxmail.com"))
+//                .version("2.0")
+//                .build();
+//    }
+//}

+ 107 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/UserSetup.java

@@ -0,0 +1,107 @@
+package com.zd.alg.iot.vmp.conf;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+@Component
+@ConfigurationProperties(prefix = "user-settings", ignoreInvalidFields = true)
+public class UserSetup {
+
+    private Boolean savePositionHistory = Boolean.FALSE;
+
+    private Boolean autoApplyPlay = Boolean.FALSE;
+
+    private Boolean seniorSdp = Boolean.FALSE;
+
+    private Long playTimeout = 60000L;
+
+    private Boolean waitTrack = Boolean.FALSE;
+
+    private Boolean interfaceAuthentication = Boolean.TRUE;
+
+    private Boolean recordPushLive = Boolean.FALSE;
+
+    private Boolean logInDatebase = Boolean.TRUE;
+
+    private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
+
+    public Boolean getSavePositionHistory() {
+        return savePositionHistory;
+    }
+
+    public Boolean isSavePositionHistory() {
+        return savePositionHistory;
+    }
+
+    public Boolean isAutoApplyPlay() {
+        return autoApplyPlay;
+    }
+
+    public Boolean isSeniorSdp() {
+        return seniorSdp;
+    }
+
+    public Long getPlayTimeout() {
+        return playTimeout;
+    }
+
+    public Boolean isWaitTrack() {
+        return waitTrack;
+    }
+
+    public Boolean isInterfaceAuthentication() {
+        return interfaceAuthentication;
+    }
+
+    public Boolean isRecordPushLive() {
+        return recordPushLive;
+    }
+
+    public List<String> getInterfaceAuthenticationExcludes() {
+        return interfaceAuthenticationExcludes;
+    }
+
+    public void setSavePositionHistory(Boolean savePositionHistory) {
+        this.savePositionHistory = savePositionHistory;
+    }
+
+    public void setAutoApplyPlay(Boolean autoApplyPlay) {
+        this.autoApplyPlay = autoApplyPlay;
+    }
+
+    public void setSeniorSdp(Boolean seniorSdp) {
+        this.seniorSdp = seniorSdp;
+    }
+
+    public void setPlayTimeout(Long playTimeout) {
+        this.playTimeout = playTimeout;
+    }
+
+    public void setWaitTrack(Boolean waitTrack) {
+        this.waitTrack = waitTrack;
+    }
+
+    public void setInterfaceAuthentication(boolean interfaceAuthentication) {
+        this.interfaceAuthentication = interfaceAuthentication;
+    }
+
+    public void setRecordPushLive(Boolean recordPushLive) {
+        this.recordPushLive = recordPushLive;
+    }
+
+    public void setInterfaceAuthenticationExcludes(List<String> interfaceAuthenticationExcludes) {
+        this.interfaceAuthenticationExcludes = interfaceAuthenticationExcludes;
+    }
+
+    public Boolean getLogInDatebase() {
+        return logInDatebase;
+    }
+
+    public void setLogInDatebase(Boolean logInDatebase) {
+        this.logInDatebase = logInDatebase;
+    }
+}

+ 25 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/VManagerConfig.java

@@ -0,0 +1,25 @@
+package com.zd.alg.iot.vmp.conf;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @Description: 获取数据库配置
+ * @author: dgs
+ * @date:   2020年5月6日 下午2:46:00
+ */
+@Configuration("vmConfig")
+public class VManagerConfig {
+
+	@Value("${spring.application.database:redis}")
+    private String database;
+
+
+    public String getDatabase() {
+        return database;
+    }
+
+    public void setDatabase(String database) {
+        this.database = database;
+    }
+}

+ 37 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/VersionConfig.java

@@ -0,0 +1,37 @@
+package com.zd.alg.iot.vmp.conf;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+@Component
+@ConfigurationProperties(prefix = "version")
+public class VersionConfig {
+
+    private String version;
+    private String artifactId;
+    private String description;
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public void setArtifactId(String artifactId) {
+        this.artifactId = artifactId;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public String getArtifactId() {
+        return artifactId;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+}

+ 37 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/VersionInfo.java

@@ -0,0 +1,37 @@
+package com.zd.alg.iot.vmp.conf;
+
+import com.zd.alg.iot.vmp.common.VersionPo;
+import com.zd.alg.iot.vmp.utils.GitUtil;
+import com.zd.alg.iot.vmp.utils.JarFileUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+@Component
+public class VersionInfo {
+
+    @Autowired
+    VersionConfig config;
+    @Autowired
+    GitUtil gitUtil;
+    @Autowired
+    JarFileUtils jarFileUtils;
+
+    public VersionPo getVersion() {
+        VersionPo versionPo = new VersionPo();
+        Map<String,String> map=jarFileUtils.readJarFile();
+        versionPo.setGIT_Revision(gitUtil.getGitCommitId());
+        versionPo.setCreate_By(map.get("Created-By"));
+        versionPo.setGIT_BRANCH(gitUtil.getBranch());
+        versionPo.setGIT_URL(gitUtil.getGitUrl());
+        versionPo.setBUILD_DATE(gitUtil.getBuildDate());
+        versionPo.setArtifactId(config.getArtifactId());
+        versionPo.setGIT_Revision_SHORT(gitUtil.getCommitIdShort());
+        versionPo.setVersion(config.getVersion());
+        versionPo.setProject(config.getDescription());
+        versionPo.setBuild_Jdk(map.get("Build-Jdk"));
+
+        return versionPo;
+    }
+}

+ 24 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/conf/WVPTimerTask.java

@@ -0,0 +1,24 @@
+package com.zd.alg.iot.vmp.conf;
+
+import com.zd.alg.iot.vmp.storager.IRedisCatchStorage;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+@Component
+public class WVPTimerTask {
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private SipConfig sipConfig;
+
+    @Autowired
+    private MediaConfig mediaConfig;
+
+    @Scheduled(cron="0/2 * *  * * ? ")   //每3秒执行一次
+    public void execute(){
+//        redisCatchStorage.updateWVPInfo();
+    }
+}

+ 256 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/SipLayer.java

@@ -0,0 +1,256 @@
+package com.zd.alg.iot.vmp.gb28181;
+
+import java.text.ParseException;
+import java.util.Properties;
+import java.util.TooManyListenersException;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.sip.*;
+import javax.sip.header.CallIdHeader;
+import javax.sip.message.Response;
+
+import com.zd.alg.iot.vmp.conf.SipConfig;
+import com.zd.alg.iot.vmp.gb28181.event.SipSubscribe;
+import com.zd.alg.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
+import gov.nist.javax.sip.SipProviderImpl;
+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.DependsOn;
+import org.springframework.stereotype.Component;
+
+import com.zd.alg.iot.vmp.gb28181.transmit.SIPProcessorFactory;
+
+import gov.nist.javax.sip.SipStackImpl;
+
+@Component
+public class SipLayer implements SipListener {
+
+	private final static Logger logger = LoggerFactory.getLogger(SipLayer.class);
+
+	@Autowired
+	private SipConfig sipConfig;
+
+	@Autowired
+	private SIPProcessorFactory processorFactory;
+
+	@Autowired
+	private SipSubscribe sipSubscribe;
+
+	private SipStackImpl sipStack;
+
+	private SipFactory sipFactory;
+
+	/**
+	 * 消息处理器线程池
+	 */
+	private ThreadPoolExecutor processThreadPool;
+
+	@Bean("initSipServer")
+	private ThreadPoolExecutor initSipServer() {
+
+		int processThreadNum = Runtime.getRuntime().availableProcessors() * 10;
+		LinkedBlockingQueue<Runnable> processQueue = new LinkedBlockingQueue<>(10000);
+		processThreadPool = new ThreadPoolExecutor(processThreadNum,processThreadNum,
+				0L,TimeUnit.MILLISECONDS,processQueue,
+				new ThreadPoolExecutor.CallerRunsPolicy());
+		return processThreadPool;
+	}
+
+	@Bean("sipFactory")
+	@DependsOn("initSipServer")
+	private SipFactory createSipFactory() {
+		sipFactory = SipFactory.getInstance();
+		sipFactory.setPathName("gov.nist");
+		return sipFactory;
+	}
+
+	@Bean("sipStack")
+	@DependsOn({"initSipServer", "sipFactory"})
+	private SipStack createSipStack() throws PeerUnavailableException {
+		Properties properties = new Properties();
+		properties.setProperty("javax.sip.STACK_NAME", "GB28181_SIP");
+		properties.setProperty("javax.sip.IP_ADDRESS", sipConfig.getMonitorIp());
+		properties.setProperty("gov.nist.javax.sip.LOG_MESSAGE_CONTENT", "false");
+		/**
+		 * sip_server_log.log 和 sip_debug_log.log public static final int TRACE_NONE =
+		 * 0; public static final int TRACE_MESSAGES = 16; public static final int
+		 * TRACE_EXCEPTION = 17; public static final int TRACE_DEBUG = 32;
+		 */
+		properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "0");
+		properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "sip_server_log");
+		properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "sip_debug_log");
+		sipStack = (SipStackImpl) sipFactory.createSipStack(properties);
+		return sipStack;
+	}
+
+	@Bean("tcpSipProvider")
+	@DependsOn("sipStack")
+	private SipProviderImpl startTcpListener() {
+		ListeningPoint tcpListeningPoint = null;
+		SipProviderImpl tcpSipProvider  = null;
+		try {
+			tcpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "TCP");
+			tcpSipProvider = (SipProviderImpl)sipStack.createSipProvider(tcpListeningPoint);
+			tcpSipProvider.addSipListener(this);
+			logger.info("Sip Server TCP 启动成功 port {" + sipConfig.getMonitorIp() + ":" + sipConfig.getPort() + "}");
+		} catch (TransportNotSupportedException e) {
+			e.printStackTrace();
+		} catch (InvalidArgumentException e) {
+			logger.error("无法使用 [ {}:{} ]作为SIP[ TCP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
+					, sipConfig.getMonitorIp(), sipConfig.getPort());
+		} catch (TooManyListenersException e) {
+			e.printStackTrace();
+		} catch (ObjectInUseException e) {
+			e.printStackTrace();
+		}
+		return tcpSipProvider;
+	}
+
+	@Bean("udpSipProvider")
+	@DependsOn("sipStack")
+	private SipProviderImpl startUdpListener() {
+		ListeningPoint udpListeningPoint = null;
+		SipProviderImpl udpSipProvider = null;
+		try {
+			udpListeningPoint = sipStack.createListeningPoint(sipConfig.getMonitorIp(), sipConfig.getPort(), "UDP");
+			udpSipProvider = (SipProviderImpl)sipStack.createSipProvider(udpListeningPoint);
+			udpSipProvider.addSipListener(this);
+//			udpSipProvider.setAutomaticDialogSupportEnabled(false);
+		} catch (TransportNotSupportedException e) {
+			e.printStackTrace();
+		} catch (InvalidArgumentException e) {
+			logger.error("无法使用 [ {}:{} ]作为SIP[ UDP ]服务,可排查: 1. sip.monitor-ip 是否为本机网卡IP; 2. sip.port 是否已被占用"
+					, sipConfig.getMonitorIp(), sipConfig.getPort());
+		} catch (TooManyListenersException e) {
+			e.printStackTrace();
+		} catch (ObjectInUseException e) {
+			e.printStackTrace();
+		}
+		logger.info("Sip Server UDP 启动成功 port [" + sipConfig.getMonitorIp() + ":" + sipConfig.getPort() + "]");
+		return udpSipProvider;
+	}
+
+	/**
+	 * SIP服务端接收消息的方法 Content 里面是GBK编码 This method is called by the SIP stack when a
+	 * new request arrives.
+	 */
+	@Override
+	public void processRequest(RequestEvent evt) {
+		logger.debug(evt.getRequest().toString());
+		// 由于jainsip是单线程程序,为提高性能并发处理
+		processThreadPool.execute(() -> {
+			if (processorFactory != null) {
+				processorFactory.createRequestProcessor(evt).process();
+			}
+		});
+	}
+
+	@Override
+	public void processResponse(ResponseEvent evt) {
+		Response response = evt.getResponse();
+		logger.debug(evt.getResponse().toString());
+		int status = response.getStatusCode();
+		if (((status >= 200) && (status < 300)) || status == 401) { // Success!
+			ISIPResponseProcessor processor = processorFactory.createResponseProcessor(evt);
+			try {
+				processor.process(evt, this, sipConfig);
+			} catch (ParseException e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+
+			if (evt.getResponse() != null && sipSubscribe.getOkSubscribesSize() > 0 ) {
+				CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME);
+				if (callIdHeader != null) {
+					SipSubscribe.Event subscribe = sipSubscribe.getOkSubscribe(callIdHeader.getCallId());
+					if (subscribe != null) {
+						subscribe.response(evt);
+					}
+				}
+			}
+		} else if ((status >= 100) && (status < 200)) {
+			// 增加其它无需回复的响应,如101、180等
+		} else {
+			logger.warn("接收到失败的response响应!status:" + status + ",message:" + response.getReasonPhrase()/* .getContent().toString()*/);
+			if (evt.getResponse() != null && sipSubscribe.getErrorSubscribesSize() > 0 ) {
+				CallIdHeader callIdHeader = (CallIdHeader)evt.getResponse().getHeader(CallIdHeader.NAME);
+				if (callIdHeader != null) {
+					SipSubscribe.Event subscribe = sipSubscribe.getErrorSubscribe(callIdHeader.getCallId());
+					if (subscribe != null) {
+						subscribe.response(evt);
+					}
+				}
+			}
+		}
+
+
+
+	}
+
+	/**
+	 * <p>
+	 * Title: processTimeout
+	 * </p>
+	 * <p>
+	 * Description:
+	 * </p>
+	 *
+	 * @param timeoutEvent
+	 */
+	@Override
+	public void processTimeout(TimeoutEvent timeoutEvent) {
+		// TODO Auto-generated method stub
+
+	}
+
+	/**
+	 * <p>
+	 * Title: processIOException
+	 * </p>
+	 * <p>
+	 * Description:
+	 * </p>
+	 *
+	 * @param exceptionEvent
+	 */
+	@Override
+	public void processIOException(IOExceptionEvent exceptionEvent) {
+		// TODO Auto-generated method stub
+	}
+
+	/**
+	 * <p>
+	 * Title: processTransactionTerminated
+	 * </p>
+	 * <p>
+	 * Description:
+	 * </p>
+	 *
+	 * @param transactionTerminatedEvent
+	 */
+	@Override
+	public void processTransactionTerminated(TransactionTerminatedEvent transactionTerminatedEvent) {
+		// TODO Auto-generated method stub
+	}
+
+	/**
+	 * <p>
+	 * Title: processDialogTerminated
+	 * </p>
+	 * <p>
+	 * Description:
+	 * </p>
+	 *
+	 * @param dialogTerminatedEvent
+	 */
+	@Override
+	public void processDialogTerminated(DialogTerminatedEvent dialogTerminatedEvent) {
+		// TODO Auto-generated method stub
+
+	}
+
+}

+ 295 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java

@@ -0,0 +1,295 @@
+/*
+ * Conditions Of Use
+ *
+ * This software was developed by employees of the National Institute of
+ * Standards and Technology (NIST), an agency of the Federal Government.
+ * Pursuant to title 15 Untied States Code Section 105, works of NIST
+ * employees are not subject to copyright protection in the United States
+ * and are considered to be in the public domain.  As a result, a formal
+ * license is not needed to use the software.
+ *
+ * This software is provided by NIST as a service and is expressly
+ * provided "AS IS."  NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
+ * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
+ * AND DATA ACCURACY.  NIST does not warrant or make any representations
+ * regarding the use of the software or the results thereof, including but
+ * not limited to the correctness, accuracy, reliability or usefulness of
+ * the software.
+ *
+ * Permission to use this software is contingent upon your acceptance
+ * of the terms of this agreement
+ *
+ * .
+ *
+ */
+package com.zd.alg.iot.vmp.gb28181.auth;
+
+import gov.nist.core.InternalErrorHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.sip.address.URI;
+import javax.sip.header.AuthorizationHeader;
+import javax.sip.header.HeaderFactory;
+import javax.sip.header.WWWAuthenticateHeader;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Date;
+import java.util.Random;
+
+/**
+ * Implements the HTTP digest authentication method server side functionality.
+ *
+ * @author M. Ranganathan
+ * @author Marc Bednarek
+ */
+
+public class DigestServerAuthenticationHelper  {
+
+    private Logger logger = LoggerFactory.getLogger(DigestServerAuthenticationHelper.class);
+
+    private MessageDigest messageDigest;
+
+    public static final String DEFAULT_ALGORITHM = "MD5";
+    public static final String DEFAULT_SCHEME = "Digest";
+
+
+
+
+    /** to hex converter */
+    private static final char[] toHex = { '0', '1', '2', '3', '4', '5', '6',
+            '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+    /**
+     * Default constructor.
+     * @throws NoSuchAlgorithmException
+     */
+    public DigestServerAuthenticationHelper()
+            throws NoSuchAlgorithmException {
+        messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
+    }
+
+    public static String toHexString(byte b[]) {
+        int pos = 0;
+        char[] c = new char[b.length * 2];
+        for (int i = 0; i < b.length; i++) {
+            c[pos++] = toHex[(b[i] >> 4) & 0x0F];
+            c[pos++] = toHex[b[i] & 0x0f];
+        }
+        return new String(c);
+    }
+
+    /**
+     * Generate the challenge string.
+     *
+     * @return a generated nonce.
+     */
+    private String generateNonce() {
+        // Get the time of day and run MD5 over it.
+        Date date = new Date();
+        long time = date.getTime();
+        Random rand = new Random();
+        long pad = rand.nextLong();
+        // String nonceString = (new Long(time)).toString()
+        //         + (new Long(pad)).toString();
+        String nonceString = Long.valueOf(time).toString()
+                + Long.valueOf(pad).toString();
+        byte mdbytes[] = messageDigest.digest(nonceString.getBytes());
+        // Convert the mdbytes array into a hex string.
+        return toHexString(mdbytes);
+    }
+
+    public Response generateChallenge(HeaderFactory headerFactory, Response response, String realm) {
+        try {
+            WWWAuthenticateHeader proxyAuthenticate = headerFactory
+                    .createWWWAuthenticateHeader(DEFAULT_SCHEME);
+            proxyAuthenticate.setParameter("realm", realm);
+            proxyAuthenticate.setParameter("qop", "auth");
+            proxyAuthenticate.setParameter("nonce", generateNonce());
+            proxyAuthenticate.setParameter("algorithm", DEFAULT_ALGORITHM);
+
+            response.setHeader(proxyAuthenticate);
+        } catch (Exception ex) {
+            InternalErrorHandler.handleException(ex);
+        }
+        return response;
+    }
+    /**
+     * Authenticate the inbound request.
+     *
+     * @param request - the request to authenticate.
+     * @param hashedPassword -- the MD5 hashed string of username:realm:plaintext password.
+     *
+     * @return true if authentication succeded and false otherwise.
+     */
+    public boolean doAuthenticateHashedPassword(Request request, String hashedPassword) {
+        AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
+        if ( authHeader == null ) return false;
+        String realm = authHeader.getRealm();
+        String username = authHeader.getUsername();
+
+        if ( username == null || realm == null ) {
+            return false;
+        }
+
+        String nonce = authHeader.getNonce();
+        URI uri = authHeader.getURI();
+        if (uri == null) {
+            return false;
+        }
+
+
+
+        String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
+        String HA1 = hashedPassword;
+
+
+        byte[] mdbytes = messageDigest.digest(A2.getBytes());
+        String HA2 = toHexString(mdbytes);
+
+        String cnonce = authHeader.getCNonce();
+        String KD = HA1 + ":" + nonce;
+        if (cnonce != null) {
+            KD += ":" + cnonce;
+        }
+        KD += ":" + HA2;
+        mdbytes = messageDigest.digest(KD.getBytes());
+        String mdString = toHexString(mdbytes);
+        String response = authHeader.getResponse();
+
+
+        return mdString.equals(response);
+    }
+
+    /**
+     * Authenticate the inbound request given plain text password.
+     *
+     * @param request - the request to authenticate.
+     * @param pass -- the plain text password.
+     *
+     * @return true if authentication succeded and false otherwise.
+     */
+    public boolean doAuthenticatePlainTextPassword(Request request, String pass) {
+        AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
+        if ( authHeader == null ) return false;
+        String realm = authHeader.getRealm().trim();
+        String username = authHeader.getUsername().trim();
+
+        if ( username == null || realm == null ) {
+            return false;
+        }
+
+        String nonce = authHeader.getNonce();
+        URI uri = authHeader.getURI();
+        if (uri == null) {
+            return false;
+        }
+        // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
+        String qop = authHeader.getQop();
+
+        // 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。
+        // 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护
+        String cnonce = authHeader.getCNonce();
+
+        // nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量
+        int nc = authHeader.getNonceCount();
+        String ncStr = String.format("%08x", nc).toUpperCase();
+        // String ncStr = new DecimalFormat("00000000").format(nc);
+        // String ncStr = new DecimalFormat("00000000").format(Integer.parseInt(nc + "", 16));
+
+        String A1 = username + ":" + realm + ":" + pass;
+        String A2 = request.getMethod().toUpperCase() + ":" + uri.toString();
+        byte mdbytes[] = messageDigest.digest(A1.getBytes());
+        String HA1 = toHexString(mdbytes);
+        logger.debug("A1: " + A1);
+        logger.debug("A2: " + A2);
+
+        mdbytes = messageDigest.digest(A2.getBytes());
+        String HA2 = toHexString(mdbytes);
+        logger.debug("HA1: " + HA1);
+        logger.debug("HA2: " + HA2);
+        // String cnonce = authHeader.getCNonce();
+        logger.debug("nonce: " + nonce);
+        logger.debug("nc: " + ncStr);
+        logger.debug("cnonce: " + cnonce);
+        logger.debug("qop: " + qop);
+        String KD = HA1 + ":" + nonce;
+
+        if (qop != null && qop.equals("auth") ) {
+            if (nc != -1) {
+                KD += ":" + ncStr;
+            }
+            if (cnonce != null) {
+                KD += ":" + cnonce;
+            }
+            KD += ":" + qop;
+        }
+        KD += ":" + HA2;
+        logger.debug("KD: " + KD);
+        mdbytes = messageDigest.digest(KD.getBytes());
+        String mdString = toHexString(mdbytes);
+        logger.debug("mdString: " + mdString);
+        String response = authHeader.getResponse();
+        logger.debug("response: " + response);
+        return mdString.equals(response);
+
+    }
+
+//     public static void main(String[] args) throws NoSuchAlgorithmException {
+//         String realm = "3402000000";
+//         String username = "44010000001180008012";
+
+
+//         String nonce = "07cab60999fbf643264ace27d3b7de8b";
+//         String uri = "sip:34020000002000000001@3402000000";
+//         // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
+//         String qop = "auth";
+
+//         // 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。
+//         // 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护
+//         //String cNonce = authHeader.getCNonce();
+
+//         // nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量
+//         int nc = 1;
+//         String ncStr = new DecimalFormat("00000000").format(nc);
+// //        String ncStr = new DecimalFormat("00000000").format(Integer.parseInt(nc + "", 16));
+//         MessageDigest messageDigest = MessageDigest.getInstance(DEFAULT_ALGORITHM);
+//         String A1 = username + ":" + realm + ":" + "12345678";
+//         String A2 = "REGISTER" + ":" + uri;
+//         byte mdbytes[] = messageDigest.digest(A1.getBytes());
+//         String HA1 = toHexString(mdbytes);
+//         System.out.println("A1: " + A1);
+//         System.out.println("A2: " + A2);
+
+//         mdbytes = messageDigest.digest(A2.getBytes());
+//         String HA2 = toHexString(mdbytes);
+//         System.out.println("HA1: " + HA1);
+//         System.out.println("HA2: " + HA2);
+//         String cnonce = "0a4f113b";
+//         System.out.println("nonce: " + nonce);
+//         System.out.println("nc: " + ncStr);
+//         System.out.println("cnonce: " + cnonce);
+//         System.out.println("qop: " + qop);
+//         String KD = HA1 + ":" + nonce;
+
+//         if (qop != null && qop.equals("auth") ) {
+//             if (nc != -1) {
+//                 KD += ":" + ncStr;
+//             }
+//             if (cnonce != null) {
+//                 KD += ":" + cnonce;
+//             }
+//             KD += ":" + qop;
+//         }
+//         KD += ":" + HA2;
+//         System.out.println("KD: " + KD);
+//         mdbytes = messageDigest.digest(KD.getBytes());
+//         String mdString = toHexString(mdbytes);
+//         System.out.println("mdString: " + mdString);
+//         String response = "4f0507d4b87cdecff04bdaf4c96348f0";
+//         System.out.println("response: " + response);
+//     }
+}

+ 31 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/auth/RegisterLogicHandler.java

@@ -0,0 +1,31 @@
+package com.zd.alg.iot.vmp.gb28181.auth;
+
+import com.zd.alg.iot.vmp.gb28181.bean.Device;
+import com.zd.alg.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Description:注册逻辑处理,当设备注册后触发逻辑。
+ * @author: dgs
+ * @date:   2020年5月8日 下午9:41:46
+ */
+@Component
+public class RegisterLogicHandler {
+
+	private Logger logger = LoggerFactory.getLogger(RegisterLogicHandler.class);
+
+	@Autowired
+	private SIPCommander cmder;
+
+	public void onRegister(Device device) {
+		// 只有第一次注册时调用查询设备信息,如需更新调用更新API接口
+		if (device.isFirsRegister()) {
+			logger.info("[{}] 首次注册,查询设备信息以及通道信息", device.getDeviceId());
+			cmder.deviceInfoQuery(device);
+			cmder.catalogQuery(device, null);
+		}
+	}
+}

+ 24 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/BaiduPoint.java

@@ -0,0 +1,24 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+public class BaiduPoint {
+
+    String bdLng;
+
+    String bdLat;
+
+    public String getBdLng() {
+        return bdLng;
+    }
+
+    public void setBdLng(String bdLng) {
+        this.bdLng = bdLng;
+    }
+
+    public String getBdLat() {
+        return bdLat;
+    }
+
+    public void setBdLat(String bdLat) {
+        this.bdLat = bdLat;
+    }
+}

+ 273 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/Device.java

@@ -0,0 +1,273 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+
+public class Device {
+
+	/**
+	 * 设备Id
+	 */
+	private String deviceId;
+
+	/**
+	 * 设备名
+	 */
+	private String name;
+
+	/**
+	 * 生产厂商
+	 */
+	private String manufacturer;
+
+	/**
+	 * 型号
+	 */
+	private String model;
+
+	/**
+	 * 固件版本
+	 */
+	private String firmware;
+
+	/**
+	 * 传输协议
+	 * UDP/TCP
+	 */
+	private String transport;
+
+	/**
+	 * 数据流传输模式
+	 * UDP:udp传输
+	 * TCP-ACTIVE:tcp主动模式
+	 * TCP-PASSIVE:tcp被动模式
+	 */
+	private String streamMode;
+
+	/**
+	 * wan地址_ip
+	 */
+	private String  ip;
+
+	/**
+	 * wan地址_port
+	 */
+	private int port;
+
+	/**
+	 * wan地址
+	 */
+	private String  hostAddress;
+
+	/**
+	 * 在线
+	 */
+	private int online;
+
+
+	/**
+	 * 注册时间
+	 */
+	private String registerTime;
+
+
+	/**
+	 * 心跳时间
+	 */
+	private String keepaliveTime;
+
+	/**
+	 * 通道个数
+	 */
+	private int channelCount;
+
+	/**
+	 * 注册有效期
+	 */
+	private int expires;
+
+	/**
+	 * 创建时间
+	 */
+	private String createTime;
+
+	/**
+	 * 更新时间
+	 */
+	private String updateTime;
+
+	/**
+	 * 设备使用的媒体id, 默认为null
+	 */
+	private String mediaServerId;
+
+	/**
+	 * 首次注册
+	 */
+	private boolean firsRegister;
+
+	/**
+	 * 字符集, 支持 utf-8 与 gb2312
+	 */
+	private String charset ;
+
+
+
+	public String getDeviceId() {
+		return deviceId;
+	}
+
+	public void setDeviceId(String deviceId) {
+		this.deviceId = deviceId;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getManufacturer() {
+		return manufacturer;
+	}
+
+	public void setManufacturer(String manufacturer) {
+		this.manufacturer = manufacturer;
+	}
+
+	public String getModel() {
+		return model;
+	}
+
+	public void setModel(String model) {
+		this.model = model;
+	}
+
+	public String getFirmware() {
+		return firmware;
+	}
+
+	public void setFirmware(String firmware) {
+		this.firmware = firmware;
+	}
+
+	public String getTransport() {
+		return transport;
+	}
+
+	public void setTransport(String transport) {
+		this.transport = transport;
+	}
+
+	public String getStreamMode() {
+		return streamMode;
+	}
+
+	public void setStreamMode(String streamMode) {
+		this.streamMode = streamMode;
+	}
+
+	public String getIp() {
+		return ip;
+	}
+
+	public void setIp(String ip) {
+		this.ip = ip;
+	}
+
+	public int getPort() {
+		return port;
+	}
+
+	public void setPort(int port) {
+		this.port = port;
+	}
+
+	public String getHostAddress() {
+		return hostAddress;
+	}
+
+	public void setHostAddress(String hostAddress) {
+		this.hostAddress = hostAddress;
+	}
+
+	public int getOnline() {
+		return online;
+	}
+
+	public void setOnline(int online) {
+		this.online = online;
+	}
+
+	public int getChannelCount() {
+		return channelCount;
+	}
+
+	public void setChannelCount(int channelCount) {
+		this.channelCount = channelCount;
+	}
+
+	public String getRegisterTime() {
+		return registerTime;
+	}
+
+	public void setRegisterTime(String registerTime) {
+		this.registerTime = registerTime;
+	}
+
+	public String getKeepaliveTime() {
+		return keepaliveTime;
+	}
+
+	public void setKeepaliveTime(String keepaliveTime) {
+		this.keepaliveTime = keepaliveTime;
+	}
+
+	public int getExpires() {
+		return expires;
+	}
+
+	public void setExpires(int expires) {
+		this.expires = expires;
+	}
+
+	public String getCreateTime() {
+		return createTime;
+	}
+
+	public void setCreateTime(String createTime) {
+		this.createTime = createTime;
+	}
+
+	public String getUpdateTime() {
+		return updateTime;
+	}
+
+	public void setUpdateTime(String updateTime) {
+		this.updateTime = updateTime;
+	}
+
+	public String getMediaServerId() {
+		return mediaServerId;
+	}
+
+	public void setMediaServerId(String mediaServerId) {
+		this.mediaServerId = mediaServerId;
+	}
+
+	public boolean isFirsRegister() {
+		return firsRegister;
+	}
+
+	public void setFirsRegister(boolean firsRegister) {
+		this.firsRegister = firsRegister;
+	}
+
+	public String getCharset() {
+		return charset;
+	}
+
+	public void setCharset(String charset) {
+		this.charset = charset;
+	}
+}

+ 124 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/DeviceAlarm.java

@@ -0,0 +1,124 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+
+public class DeviceAlarm {
+
+	/**
+	 * 数据库id
+	 */
+	private String id;
+
+	/**
+	 * 设备Id
+	 */
+	private String deviceId;
+
+	/**
+	 * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级 警情-
+	 */
+	private String alarmPriority;
+
+	/**
+	 * 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警,
+	 * 7其他报警;可以为直接组合如12为电话报警或 设备报警-
+	 */
+	private String alarmMethod;
+
+	/**
+	 * 报警时间
+	 */
+	private String alarmTime;
+
+	/**
+	 * 报警内容描述
+	 */
+	private String alarmDescription;
+
+	/**
+	 * 经度
+	 */
+	private double longitude;
+
+	/**
+	 * 纬度
+	 */
+	private double latitude;
+
+	/**
+	 * 报警类型
+	 */
+	private String alarmType;
+
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getDeviceId() {
+		return deviceId;
+	}
+
+	public void setDeviceId(String deviceId) {
+		this.deviceId = deviceId;
+	}
+
+	public String getAlarmPriority() {
+		return alarmPriority;
+	}
+
+	public void setAlarmPriority(String alarmPriority) {
+		this.alarmPriority = alarmPriority;
+	}
+
+	public String getAlarmMethod() {
+		return alarmMethod;
+	}
+
+	public void setAlarmMethod(String alarmMethod) {
+		this.alarmMethod = alarmMethod;
+	}
+
+	public String getAlarmTime() {
+		return alarmTime;
+	}
+
+	public void setAlarmTime(String alarmTime) {
+		this.alarmTime = alarmTime;
+	}
+
+	public String getAlarmDescription() {
+		return alarmDescription;
+	}
+
+	public void setAlarmDescription(String alarmDescription) {
+		this.alarmDescription = alarmDescription;
+	}
+
+	public double getLongitude() {
+		return longitude;
+	}
+
+	public void setLongitude(double longitude) {
+		this.longitude = longitude;
+	}
+
+	public double getLatitude() {
+		return latitude;
+	}
+
+	public void setLatitude(double latitude) {
+		this.latitude = latitude;
+	}
+
+	public String getAlarmType() {
+		return alarmType;
+	}
+
+	public void setAlarmType(String alarmType) {
+		this.alarmType = alarmType;
+	}
+}

+ 432 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/DeviceChannel.java

@@ -0,0 +1,432 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+public class DeviceChannel {
+
+
+
+	/**
+	 * 通道id
+	 */
+	private String channelId;
+
+	/**
+	 * 设备id
+	 */
+	private String deviceId;
+
+	/**
+	 * 通道名
+	 */
+	private String name;
+
+	/**
+	 * 生产厂商
+	 */
+	private String manufacture;
+
+	/**
+	 * 型号
+	 */
+	private String model;
+
+	/**
+	 * 设备归属
+	 */
+	private String owner;
+
+	/**
+	 * 行政区域
+	 */
+	private String civilCode;
+
+	/**
+	 * 警区
+	 */
+	private String block;
+
+	/**
+	 * 安装地址
+	 */
+	private String address;
+
+	/**
+	 * 是否有子设备 1有, 0没有
+	 */
+	private int parental;
+
+	/**
+	 * 父级id
+	 */
+	private String parentId;
+
+	/**
+	 * 信令安全模式  缺省为0; 0:不采用; 2: S/MIME签名方式; 3: S/ MIME加密签名同时采用方式; 4:数字摘要方式
+	 */
+	private int safetyWay;
+
+	/**
+	 * 注册方式 缺省为1;1:符合IETFRFC3261标准的认证注册模 式; 2:基于口令的双向认证注册模式; 3:基于数字证书的双向认证注册模式
+	 */
+	private int registerWay;
+
+	/**
+	 * 证书序列号
+	 */
+	private String certNum;
+
+	/**
+	 * 证书有效标识 缺省为0;证书有效标识:0:无效1: 有效
+	 */
+	private int certifiable;
+
+	/**
+	 * 证书无效原因码
+	 */
+	private int errCode;
+
+	/**
+	 * 证书终止有效期
+	 */
+	private String endTime;
+
+	/**
+	 * 保密属性 缺省为0; 0:不涉密, 1:涉密
+	 */
+	private String secrecy;
+
+	/**
+	 * IP地址
+	 */
+	private String ipAddress;
+
+	/**
+	 * 端口号
+	 */
+	private int port;
+
+	/**
+	 * 密码
+	 */
+	private String password;
+
+	/**
+	 * 云台类型
+	 */
+	private int PTZType;
+
+	/**
+	 * 云台类型描述字符串
+	 */
+	private String PTZTypeText;
+
+	/**
+	 * 创建时间
+	 */
+	private String createTime;
+
+	/**
+	 * 更新时间
+	 */
+	private String updateTime;
+
+	/**
+	 * 在线/离线
+	 * 1在线,0离线
+	 * 默认在线
+	 * 信令:
+	 * <Status>ON</Status>
+	 * <Status>OFF</Status>
+	 * 遇到过NVR下的IPC下发信令可以推流, 但是 Status 响应 OFF
+	 */
+	private int status;
+
+	/**
+	 * 经度
+	 */
+	private double longitude;
+
+	/**
+	 * 纬度
+	 */
+	private double latitude;
+
+	/**
+	 * 子设备数
+	 */
+	private int subCount;
+
+	/**
+	 * 流唯一编号,存在表示正在直播
+	 */
+	private String  streamId;
+
+	/**
+	 *  是否含有音频
+	 */
+	private boolean hasAudio;
+
+	public String getDeviceId() {
+		return deviceId;
+	}
+
+	public void setDeviceId(String deviceId) {
+		this.deviceId = deviceId;
+	}
+
+	public void setPTZType(int PTZType) {
+		this.PTZType = PTZType;
+		switch (PTZType) {
+			case 0:
+				this.PTZTypeText = "未知";
+				break;
+			case 1:
+				this.PTZTypeText = "球机";
+				break;
+			case 2:
+				this.PTZTypeText = "半球";
+				break;
+			case 3:
+				this.PTZTypeText = "固定枪机";
+				break;
+			case 4:
+				this.PTZTypeText = "遥控枪机";
+				break;
+		}
+	}
+
+	public String getChannelId() {
+		return channelId;
+	}
+
+	public void setChannelId(String channelId) {
+		this.channelId = channelId;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getManufacture() {
+		return manufacture;
+	}
+
+	public void setManufacture(String manufacture) {
+		this.manufacture = manufacture;
+	}
+
+	public String getModel() {
+		return model;
+	}
+
+	public void setModel(String model) {
+		this.model = model;
+	}
+
+	public String getOwner() {
+		return owner;
+	}
+
+	public void setOwner(String owner) {
+		this.owner = owner;
+	}
+
+	public String getCivilCode() {
+		return civilCode;
+	}
+
+	public void setCivilCode(String civilCode) {
+		this.civilCode = civilCode;
+	}
+
+	public String getBlock() {
+		return block;
+	}
+
+	public void setBlock(String block) {
+		this.block = block;
+	}
+
+	public String getAddress() {
+		return address;
+	}
+
+	public void setAddress(String address) {
+		this.address = address;
+	}
+
+	public int getParental() {
+		return parental;
+	}
+
+	public void setParental(int parental) {
+		this.parental = parental;
+	}
+
+	public String getParentId() {
+		return parentId;
+	}
+
+	public void setParentId(String parentId) {
+		this.parentId = parentId;
+	}
+
+	public int getSafetyWay() {
+		return safetyWay;
+	}
+
+	public void setSafetyWay(int safetyWay) {
+		this.safetyWay = safetyWay;
+	}
+
+	public int getRegisterWay() {
+		return registerWay;
+	}
+
+	public void setRegisterWay(int registerWay) {
+		this.registerWay = registerWay;
+	}
+
+	public String getCertNum() {
+		return certNum;
+	}
+
+	public void setCertNum(String certNum) {
+		this.certNum = certNum;
+	}
+
+	public int getCertifiable() {
+		return certifiable;
+	}
+
+	public void setCertifiable(int certifiable) {
+		this.certifiable = certifiable;
+	}
+
+	public int getErrCode() {
+		return errCode;
+	}
+
+	public void setErrCode(int errCode) {
+		this.errCode = errCode;
+	}
+
+	public String getEndTime() {
+		return endTime;
+	}
+
+	public void setEndTime(String endTime) {
+		this.endTime = endTime;
+	}
+
+	public String getSecrecy() {
+		return secrecy;
+	}
+
+	public void setSecrecy(String secrecy) {
+		this.secrecy = secrecy;
+	}
+
+	public String getIpAddress() {
+		return ipAddress;
+	}
+
+	public void setIpAddress(String ipAddress) {
+		this.ipAddress = ipAddress;
+	}
+
+	public int getPort() {
+		return port;
+	}
+
+	public void setPort(int port) {
+		this.port = port;
+	}
+
+	public String getPassword() {
+		return password;
+	}
+
+	public void setPassword(String password) {
+		this.password = password;
+	}
+
+	public int getPTZType() {
+		return PTZType;
+	}
+
+	public String getPTZTypeText() {
+		return PTZTypeText;
+	}
+
+	public void setPTZTypeText(String PTZTypeText) {
+		this.PTZTypeText = PTZTypeText;
+	}
+
+	public int getStatus() {
+		return status;
+	}
+
+	public void setStatus(int status) {
+		this.status = status;
+	}
+
+	public double getLongitude() {
+		return longitude;
+	}
+
+	public void setLongitude(double longitude) {
+		this.longitude = longitude;
+	}
+
+	public double getLatitude() {
+		return latitude;
+	}
+
+	public void setLatitude(double latitude) {
+		this.latitude = latitude;
+	}
+
+	public int getSubCount() {
+		return subCount;
+	}
+
+	public void setSubCount(int subCount) {
+		this.subCount = subCount;
+	}
+
+	public boolean isHasAudio() {
+		return hasAudio;
+	}
+
+	public void setHasAudio(boolean hasAudio) {
+		this.hasAudio = hasAudio;
+	}
+
+	public String getStreamId() {
+		return streamId;
+	}
+
+	public void setStreamId(String streamId) {
+		this.streamId = streamId;
+	}
+
+	public String getCreateTime() {
+		return createTime;
+	}
+
+	public void setCreateTime(String createTime) {
+		this.createTime = createTime;
+	}
+
+	public String getUpdateTime() {
+		return updateTime;
+	}
+
+	public void setUpdateTime(String updateTime) {
+		this.updateTime = updateTime;
+	}
+}

+ 89 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/GbStream.java

@@ -0,0 +1,89 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+/**
+ * 直播流关联国标上级平台
+ */
+public class GbStream extends PlatformGbStream{
+
+    private String app;
+    private String stream;
+    private String gbId;
+    private String name;
+    private String mediaServerId;
+    private double longitude;
+    private double latitude;
+    private String streamType;
+    private boolean status;
+
+    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;
+    }
+
+    public String getGbId() {
+        return gbId;
+    }
+
+    public void setGbId(String gbId) {
+        this.gbId = gbId;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public double getLongitude() {
+        return longitude;
+    }
+
+    public void setLongitude(double longitude) {
+        this.longitude = longitude;
+    }
+
+    public double getLatitude() {
+        return latitude;
+    }
+
+    public void setLatitude(double latitude) {
+        this.latitude = latitude;
+    }
+
+    public String getStreamType() {
+        return streamType;
+    }
+
+    public void setStreamType(String streamType) {
+        this.streamType = streamType;
+    }
+
+    public boolean isStatus() {
+        return status;
+    }
+
+    public void setStatus(boolean status) {
+        this.status = status;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+}

+ 35 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/Host.java

@@ -0,0 +1,35 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+
+
+public class Host {
+
+	private String ip;
+	private int port;
+	private String address;
+
+
+	public String getIp() {
+		return ip;
+	}
+
+	public void setIp(String ip) {
+		this.ip = ip;
+	}
+
+	public int getPort() {
+		return port;
+	}
+
+	public void setPort(int port) {
+		this.port = port;
+	}
+
+	public String getAddress() {
+		return address;
+	}
+
+	public void setAddress(String address) {
+		this.address = address;
+	}
+}

+ 166 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/MobilePosition.java

@@ -0,0 +1,166 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+/**
+ * @Description: 移动位置bean
+ * @author: lawrencehj
+ * @date: 2021年1月23日
+ */
+
+public class MobilePosition {
+    /**
+     * 设备Id
+     */
+    private String deviceId;
+
+    /**
+     * 设备名称
+     */
+    private String deviceName;
+
+    /**
+     * 通知时间
+     */
+    private String time;
+
+    /**
+     * 经度
+     */
+    private double longitude;
+
+    /**
+     * 纬度
+     */
+    private double latitude;
+
+    /**
+     * 海拔高度
+     */
+    private double altitude;
+
+    /**
+     * 速度
+     */
+    private double speed;
+
+    /**
+     * 方向
+     */
+    private double direction;
+
+    /**
+     * 位置信息上报来源(Mobile Position、GPS Alarm)
+     */
+    private String reportSource;
+
+    /**
+     * 国内地理坐标系(GCJ-02 / BD-09)
+     */
+    private String GeodeticSystem;
+
+    /**
+     * 国内坐标系:经度坐标
+     */
+    private String cnLng;
+
+    /**
+     * 国内坐标系:纬度坐标
+     */
+    private String cnLat;
+
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public String getDeviceName() {
+        return deviceName;
+    }
+
+    public void setDeviceName(String deviceName) {
+        this.deviceName = deviceName;
+    }
+
+    public String getTime() {
+        return time;
+    }
+
+    public void setTime(String time) {
+        this.time = time;
+    }
+
+    public double getLongitude() {
+        return longitude;
+    }
+
+    public void setLongitude(double longitude) {
+        this.longitude = longitude;
+    }
+
+    public double getLatitude() {
+        return latitude;
+    }
+
+    public void setLatitude(double latitude) {
+        this.latitude = latitude;
+    }
+
+    public double getAltitude() {
+        return altitude;
+    }
+
+    public void setAltitude(double altitude) {
+        this.altitude = altitude;
+    }
+
+    public double getSpeed() {
+        return speed;
+    }
+
+    public void setSpeed(double speed) {
+        this.speed = speed;
+    }
+
+    public double getDirection() {
+        return direction;
+    }
+
+    public void setDirection(double direction) {
+        this.direction = direction;
+    }
+
+    public String getReportSource() {
+        return reportSource;
+    }
+
+    public void setReportSource(String reportSource) {
+        this.reportSource = reportSource;
+    }
+
+    public String getGeodeticSystem() {
+        return GeodeticSystem;
+    }
+
+    public void setGeodeticSystem(String geodeticSystem) {
+        GeodeticSystem = geodeticSystem;
+    }
+
+    public String getCnLng() {
+        return cnLng;
+    }
+
+    public void setCnLng(String cnLng) {
+        this.cnLng = cnLng;
+    }
+
+    public String getCnLat() {
+        return cnLat;
+    }
+
+    public void setCnLat(String cnLat) {
+        this.cnLat = cnLat;
+    }
+}

+ 267 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/ParentPlatform.java

@@ -0,0 +1,267 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+public class ParentPlatform {
+
+    /**
+     * id
+     */
+    private Integer id;
+
+    /**
+     * 是否启用
+     */
+    private boolean enable;
+
+    /**
+     * 名称
+     */
+    private String name;
+
+    /**
+     * SIP服务国标编码
+     */
+    private String serverGBId;
+
+    /**
+     * SIP服务国标域
+     */
+    private String serverGBDomain;
+
+    /**
+     * SIP服务IP
+     */
+    private String serverIP;
+
+    /**
+     * SIP服务端口
+     */
+    private int serverPort;
+
+    /**
+     * 设备国标编号
+     */
+    private String deviceGBId;
+
+    /**
+     * 设备ip
+     */
+    private String deviceIp;
+
+    /**
+     * 设备端口
+     */
+    private String devicePort;
+
+    /**
+     * SIP认证用户名(默认使用设备国标编号)
+     */
+    private String username;
+
+    /**
+     * SIP认证密码
+     */
+    private String password;
+
+    /**
+     * 注册周期 (秒)
+     */
+    private String expires;
+
+    /**
+     * 心跳周期(秒)
+     */
+    private String keepTimeout;
+
+    /**
+     * 传输协议
+     * UDP/TCP
+     */
+    private String transport;
+
+    /**
+     * 字符集
+     */
+    private String characterSet;
+
+    /**
+     * 允许云台控制
+     */
+    private boolean ptz;
+
+    /**
+     * RTCP流保活
+     * TODO 预留, 暂不实现
+     */
+    private boolean rtcp;
+
+    /**
+     * 在线状态
+     */
+    private boolean status;
+
+    /**
+     * 在线状态
+     */
+    private int channelCount;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public boolean isEnable() {
+        return enable;
+    }
+
+    public void setEnable(boolean enable) {
+        this.enable = enable;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getServerGBId() {
+        return serverGBId;
+    }
+
+    public void setServerGBId(String serverGBId) {
+        this.serverGBId = serverGBId;
+    }
+
+    public String getServerGBDomain() {
+        return serverGBDomain;
+    }
+
+    public void setServerGBDomain(String serverGBDomain) {
+        this.serverGBDomain = serverGBDomain;
+    }
+
+    public String getServerIP() {
+        return serverIP;
+    }
+
+    public void setServerIP(String serverIP) {
+        this.serverIP = serverIP;
+    }
+
+    public int getServerPort() {
+        return serverPort;
+    }
+
+    public void setServerPort(int serverPort) {
+        this.serverPort = serverPort;
+    }
+
+    public String getDeviceGBId() {
+        return deviceGBId;
+    }
+
+    public void setDeviceGBId(String deviceGBId) {
+        this.deviceGBId = deviceGBId;
+    }
+
+    public String getDeviceIp() {
+        return deviceIp;
+    }
+
+    public void setDeviceIp(String deviceIp) {
+        this.deviceIp = deviceIp;
+    }
+
+    public String getDevicePort() {
+        return devicePort;
+    }
+
+    public void setDevicePort(String devicePort) {
+        this.devicePort = devicePort;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getExpires() {
+        return expires;
+    }
+
+    public void setExpires(String expires) {
+        this.expires = expires;
+    }
+
+    public String getKeepTimeout() {
+        return keepTimeout;
+    }
+
+    public void setKeepTimeout(String keepTimeout) {
+        this.keepTimeout = keepTimeout;
+    }
+
+    public String getTransport() {
+        return transport;
+    }
+
+    public void setTransport(String transport) {
+        this.transport = transport;
+    }
+
+    public String getCharacterSet() {
+        return characterSet;
+    }
+
+    public void setCharacterSet(String characterSet) {
+        this.characterSet = characterSet;
+    }
+
+    public boolean isPtz() {
+        return ptz;
+    }
+
+    public void setPtz(boolean ptz) {
+        this.ptz = ptz;
+    }
+
+    public boolean isRtcp() {
+        return rtcp;
+    }
+
+    public void setRtcp(boolean rtcp) {
+        this.rtcp = rtcp;
+    }
+
+    public boolean isStatus() {
+        return status;
+    }
+
+    public void setStatus(boolean status) {
+        this.status = status;
+    }
+
+    public int getChannelCount() {
+        return channelCount;
+    }
+
+    public void setChannelCount(int channelCount) {
+        this.channelCount = channelCount;
+    }
+
+}

+ 56 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/ParentPlatformCatch.java

@@ -0,0 +1,56 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+public class ParentPlatformCatch {
+
+    private String id;
+
+    // 心跳未回复次数
+    private int keepAliveReply;
+
+    // 注册未回复次数
+    private int registerAliveReply;
+
+    private String callId;
+
+    private ParentPlatform parentPlatform;
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public int getKeepAliveReply() {
+        return keepAliveReply;
+    }
+
+    public void setKeepAliveReply(int keepAliveReply) {
+        this.keepAliveReply = keepAliveReply;
+    }
+
+    public int getRegisterAliveReply() {
+        return registerAliveReply;
+    }
+
+    public void setRegisterAliveReply(int registerAliveReply) {
+        this.registerAliveReply = registerAliveReply;
+    }
+
+    public ParentPlatform getParentPlatform() {
+        return parentPlatform;
+    }
+
+    public void setParentPlatform(ParentPlatform parentPlatform) {
+        this.parentPlatform = parentPlatform;
+    }
+
+    public String getCallId() {
+        return callId;
+    }
+
+    public void setCallId(String callId) {
+        this.callId = callId;
+    }
+}

+ 32 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/PlatformGbStream.java

@@ -0,0 +1,32 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+public class PlatformGbStream {
+    private String app;
+    private String stream;
+    private String platformId;
+
+    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;
+    }
+
+    public String getPlatformId() {
+        return platformId;
+    }
+
+    public void setPlatformId(String platformId) {
+        this.platformId = platformId;
+    }
+
+}

+ 15 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/PlatformRegister.java

@@ -0,0 +1,15 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+public class PlatformRegister {
+
+    // 未回复次数
+    private int reply;
+
+    public int getReply() {
+        return reply;
+    }
+
+    public void setReply(int reply) {
+        this.reply = reply;
+    }
+}

+ 55 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/RecordInfo.java

@@ -0,0 +1,55 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+
+//import gov.nist.javax.sip.header.SIPDate;
+
+import java.util.List;
+
+/**
+ * @Description:设备录像信息bean
+ * @author: dgs
+ * @date:   2020年5月8日 下午2:05:56
+ */
+public class RecordInfo {
+
+	private String deviceId;
+
+	private String name;
+
+	private int sumNum;
+
+	private List<RecordItem> recordList;
+
+	public String getDeviceId() {
+		return deviceId;
+	}
+
+	public void setDeviceId(String deviceId) {
+		this.deviceId = deviceId;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public int getSumNum() {
+		return sumNum;
+	}
+
+	public void setSumNum(int sumNum) {
+		this.sumNum = sumNum;
+	}
+
+	public List<RecordItem> getRecordList() {
+		return recordList;
+	}
+
+	public void setRecordList(List<RecordItem> recordList) {
+		this.recordList = recordList;
+	}
+
+}

+ 122 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/RecordItem.java

@@ -0,0 +1,122 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+
+import javax.validation.constraints.NotNull;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @Description:设备录像bean
+ * @author: dgs
+ * @date:   2020年5月8日 下午2:06:54
+ */
+public class RecordItem  implements Comparable<RecordItem>{
+
+	private String deviceId;
+
+	private String name;
+
+	private String filePath;
+
+	private String address;
+
+	private String startTime;
+
+	private String endTime;
+
+	private int secrecy;
+
+	private String type;
+
+	private String recorderId;
+
+	public String getDeviceId() {
+		return deviceId;
+	}
+
+	public void setDeviceId(String deviceId) {
+		this.deviceId = deviceId;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getFilePath() {
+		return filePath;
+	}
+
+	public void setFilePath(String filePath) {
+		this.filePath = filePath;
+	}
+
+	public String getAddress() {
+		return address;
+	}
+
+	public void setAddress(String address) {
+		this.address = address;
+	}
+
+	public String getStartTime() {
+		return startTime;
+	}
+
+	public void setStartTime(String startTime) {
+		this.startTime = startTime;
+	}
+
+	public String getEndTime() {
+		return endTime;
+	}
+
+	public void setEndTime(String endTime) {
+		this.endTime = endTime;
+	}
+
+	public int getSecrecy() {
+		return secrecy;
+	}
+
+	public void setSecrecy(int secrecy) {
+		this.secrecy = secrecy;
+	}
+
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	public String getRecorderId() {
+		return recorderId;
+	}
+
+	public void setRecorderId(String recorderId) {
+		this.recorderId = recorderId;
+	}
+
+	@Override
+	public int compareTo(@NotNull RecordItem recordItem) {
+		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+		try {
+			Date startTime_now = sdf.parse(startTime);
+			Date startTime_param = sdf.parse(recordItem.getStartTime());
+			if (startTime_param.compareTo(startTime_now) > 0) {
+				return -1;
+			}else {
+				return 1;
+			}
+		} catch (ParseException e) {
+			e.printStackTrace();
+		}
+		return 0;
+	}
+}

+ 177 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/SendRtpItem.java

@@ -0,0 +1,177 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+public class SendRtpItem {
+
+    /**
+     * 推流ip
+     */
+    private String ip;
+
+    /**
+     * 推流端口
+     */
+    private int port;
+
+    /**
+     * 推流标识
+     */
+    private String ssrc;
+
+    /**
+     * 平台id
+     */
+    private String platformId;
+
+     /**
+     * 对应设备id
+     */
+    private String deviceId;
+
+    /**
+     * 直播流的应用名
+     */
+    private String app;
+
+   /**
+     * 通道id
+     */
+    private String channelId;
+
+    /**
+     * 推流状态
+     * 0 等待设备推流上来
+     * 1 等待上级平台回复ack
+     * 2 推流中
+     */
+    private int status = 0;
+
+
+    /**
+     * 设备推流的streamId
+     */
+    private String streamId;
+
+    /**
+     * 是否为tcp
+     */
+    private boolean tcp;
+
+    /**
+     * 是否为tcp主动模式
+     */
+    private boolean tcpActive;
+
+    /**
+     * 自己推流使用的端口
+     */
+    private int localPort;
+
+    /**
+     * 使用的流媒体
+     */
+    private String mediaServerId;
+
+    public String getIp() {
+        return ip;
+    }
+
+    public void setIp(String ip) {
+        this.ip = ip;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public String getSsrc() {
+        return ssrc;
+    }
+
+    public void setSsrc(String ssrc) {
+        this.ssrc = ssrc;
+    }
+
+    public String getPlatformId() {
+        return platformId;
+    }
+
+    public void setPlatformId(String platformId) {
+        this.platformId = platformId;
+    }
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public String getChannelId() {
+        return channelId;
+    }
+
+    public void setChannelId(String channelId) {
+        this.channelId = channelId;
+    }
+
+    public int getStatus() {
+        return status;
+    }
+
+    public void setStatus(int status) {
+        this.status = status;
+    }
+
+    public String getApp() {
+        return app;
+    }
+
+    public void setApp(String app) {
+        this.app = app;
+    }
+
+    public String getStreamId() {
+        return streamId;
+    }
+
+    public void setStreamId(String streamId) {
+        this.streamId = streamId;
+    }
+
+    public boolean isTcp() {
+        return tcp;
+    }
+
+    public void setTcp(boolean tcp) {
+        this.tcp = tcp;
+    }
+
+    public int getLocalPort() {
+        return localPort;
+    }
+
+    public void setLocalPort(int localPort) {
+        this.localPort = localPort;
+    }
+
+    public boolean isTcpActive() {
+        return tcpActive;
+    }
+
+    public void setTcpActive(boolean tcpActive) {
+        this.tcpActive = tcpActive;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+}

+ 68 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/SsrcTransaction.java

@@ -0,0 +1,68 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+public class SsrcTransaction {
+
+    private String deviceId;
+    private String channelId;
+    private String ssrc;
+    private String streamId;
+    private byte[] transaction;
+    private byte[] dialog;
+    private String mediaServerId;
+
+    public String getDeviceId() {
+        return deviceId;
+    }
+
+    public void setDeviceId(String deviceId) {
+        this.deviceId = deviceId;
+    }
+
+    public String getChannelId() {
+        return channelId;
+    }
+
+    public void setChannelId(String channelId) {
+        this.channelId = channelId;
+    }
+
+    public String getSsrc() {
+        return ssrc;
+    }
+
+    public void setSsrc(String ssrc) {
+        this.ssrc = ssrc;
+    }
+
+    public String getStreamId() {
+        return streamId;
+    }
+
+    public void setStreamId(String streamId) {
+        this.streamId = streamId;
+    }
+
+    public byte[] getTransaction() {
+        return transaction;
+    }
+
+    public void setTransaction(byte[] transaction) {
+        this.transaction = transaction;
+    }
+
+    public byte[] getDialog() {
+        return dialog;
+    }
+
+    public void setDialog(byte[] dialog) {
+        this.dialog = dialog;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+}

+ 149 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/bean/WvpSipDate.java

@@ -0,0 +1,149 @@
+package com.zd.alg.iot.vmp.gb28181.bean;
+
+import gov.nist.core.InternalErrorHandler;
+import gov.nist.javax.sip.header.SIPDate;
+
+import java.util.*;
+
+/**
+ * 重写jain sip的SIPDate解决与国标时间格式不一致的问题
+ */
+public class WvpSipDate extends SIPDate {
+
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+
+    private Calendar javaCal;
+
+    public WvpSipDate(long timeMillis) {
+        this.javaCal = new GregorianCalendar(TimeZone.getDefault(), Locale.getDefault());
+        Date date = new Date(timeMillis);
+        this.javaCal.setTime(date);
+        this.wkday = this.javaCal.get(7);
+        switch(this.wkday) {
+            case 1:
+                this.sipWkDay = "Sun";
+                break;
+            case 2:
+                this.sipWkDay = "Mon";
+                break;
+            case 3:
+                this.sipWkDay = "Tue";
+                break;
+            case 4:
+                this.sipWkDay = "Wed";
+                break;
+            case 5:
+                this.sipWkDay = "Thu";
+                break;
+            case 6:
+                this.sipWkDay = "Fri";
+                break;
+            case 7:
+                this.sipWkDay = "Sat";
+                break;
+            default:
+                InternalErrorHandler.handleException("No date map for wkday " + this.wkday);
+        }
+
+        this.day = this.javaCal.get(5);
+        this.month = this.javaCal.get(2);
+        switch(this.month) {
+            case 0:
+                this.sipMonth = "Jan";
+                break;
+            case 1:
+                this.sipMonth = "Feb";
+                break;
+            case 2:
+                this.sipMonth = "Mar";
+                break;
+            case 3:
+                this.sipMonth = "Apr";
+                break;
+            case 4:
+                this.sipMonth = "May";
+                break;
+            case 5:
+                this.sipMonth = "Jun";
+                break;
+            case 6:
+                this.sipMonth = "Jul";
+                break;
+            case 7:
+                this.sipMonth = "Aug";
+                break;
+            case 8:
+                this.sipMonth = "Sep";
+                break;
+            case 9:
+                this.sipMonth = "Oct";
+                break;
+            case 10:
+                this.sipMonth = "Nov";
+                break;
+            case 11:
+                this.sipMonth = "Dec";
+                break;
+            default:
+                InternalErrorHandler.handleException("No date map for month " + this.month);
+        }
+
+        this.year = this.javaCal.get(1);
+        this.hour = this.javaCal.get(11);
+        this.minute = this.javaCal.get(12);
+        this.second = this.javaCal.get(13);
+    }
+
+    @Override
+    public StringBuilder encode(StringBuilder var1) {
+        String var2;
+        if (this.month < 9) {
+            var2 = "0" + (this.month + 1);
+        } else {
+            var2 = "" + (this.month + 1);
+        }
+
+        String var3;
+        if (this.day < 10) {
+            var3 = "0" + this.day;
+        } else {
+            var3 = "" + this.day;
+        }
+
+        String var4;
+        if (this.hour < 10) {
+            var4 = "0" + this.hour;
+        } else {
+            var4 = "" + this.hour;
+        }
+
+        String var5;
+        if (this.minute < 10) {
+            var5 = "0" + this.minute;
+        } else {
+            var5 = "" + this.minute;
+        }
+
+        String var6;
+        if (this.second < 10) {
+            var6 = "0" + this.second;
+        } else {
+            var6 = "" + this.second;
+        }
+
+        int var8 = this.javaCal.get(14);
+        String var7;
+        if (var8 < 10) {
+            var7 = "00" + var8;
+        } else if (var8 < 100) {
+            var7 = "0" + var8;
+        } else {
+            var7 = "" + var8;
+        }
+
+        return var1.append(this.year).append("-").append(var2).append("-").append(var3).append("T").append(var4).append(":").append(var5).append(":").append(var6).append(".").append(var7);
+    }
+}

+ 24 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/DeviceOffLineDetector.java

@@ -0,0 +1,24 @@
+package com.zd.alg.iot.vmp.gb28181.event;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.zd.alg.iot.vmp.common.VideoManagerConstants;
+import com.zd.alg.iot.vmp.utils.redis.RedisUtil;
+
+/**
+ * @Description:设备离在线状态检测器,用于检测设备状态
+ * @author: dgs
+ * @date:   2021年10月13日 下午2:40:29
+ */
+@Component
+public class DeviceOffLineDetector {
+
+	@Autowired
+    private RedisUtil redis;
+
+	public boolean isOnline(String deviceId) {
+		String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + deviceId;
+		return redis.hasKey(key);
+	}
+}

+ 70 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/EventPublisher.java

@@ -0,0 +1,70 @@
+package com.zd.alg.iot.vmp.gb28181.event;
+
+import com.zd.alg.iot.vmp.gb28181.event.offline.OfflineEvent;
+import com.zd.alg.iot.vmp.gb28181.event.platformKeepaliveExpire.PlatformKeepaliveExpireEvent;
+import com.zd.alg.iot.vmp.gb28181.event.platformNotRegister.PlatformNotRegisterEvent;
+import com.zd.alg.iot.vmp.gb28181.bean.Device;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Component;
+
+import com.zd.alg.iot.vmp.gb28181.bean.DeviceAlarm;
+import com.zd.alg.iot.vmp.gb28181.event.alarm.AlarmEvent;
+import com.zd.alg.iot.vmp.gb28181.event.online.OnlineEvent;
+
+/**
+ * @Description:Event事件通知推送器,支持推送在线事件、离线事件
+ * @author: dgs
+ * @date:   2020年5月6日 上午11:30:50
+ */
+@Component
+public class EventPublisher {
+
+	@Autowired
+    private ApplicationEventPublisher applicationEventPublisher;
+
+	public void onlineEventPublish(Device device, String from) {
+		OnlineEvent onEvent = new OnlineEvent(this);
+		onEvent.setDevice(device);
+		onEvent.setFrom(from);
+        applicationEventPublisher.publishEvent(onEvent);
+    }
+
+	public void outlineEventPublish(String deviceId, String from){
+		OfflineEvent outEvent = new OfflineEvent(this);
+		outEvent.setDeviceId(deviceId);
+		outEvent.setFrom(from);
+        applicationEventPublisher.publishEvent(outEvent);
+    }
+
+	/**
+	 * 平台心跳到期事件
+	 * @param platformGbId
+	 */
+	public void platformKeepaliveExpireEventPublish(String platformGbId){
+		PlatformKeepaliveExpireEvent platformNotRegisterEvent = new PlatformKeepaliveExpireEvent(this);
+		platformNotRegisterEvent.setPlatformGbID(platformGbId);
+        applicationEventPublisher.publishEvent(platformNotRegisterEvent);
+    }
+
+	/**
+	 * 平台未注册事件
+	 * @param platformGbId
+	 */
+	public void platformNotRegisterEventPublish(String platformGbId){
+		PlatformNotRegisterEvent platformNotRegisterEvent = new PlatformNotRegisterEvent(this);
+		platformNotRegisterEvent.setPlatformGbID(platformGbId);
+        applicationEventPublisher.publishEvent(platformNotRegisterEvent);
+	}
+
+	/**
+	 * 设备报警事件
+	 * @param deviceAlarm
+	 */
+	public void deviceAlarmEventPublish(DeviceAlarm deviceAlarm) {
+		AlarmEvent alarmEvent = new AlarmEvent(this);
+		alarmEvent.setAlarmInfo(deviceAlarm);
+		applicationEventPublisher.publishEvent(alarmEvent);
+	}
+
+}

+ 42 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/SipSubscribe.java

@@ -0,0 +1,42 @@
+package com.zd.alg.iot.vmp.gb28181.event;
+
+import org.springframework.stereotype.Component;
+
+import javax.sip.ResponseEvent;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Component
+public class SipSubscribe {
+
+    private Map<String, Event> errorSubscribes = new ConcurrentHashMap<>();
+
+    private Map<String, Event> okSubscribes = new ConcurrentHashMap<>();
+
+    public interface Event {
+        void response(ResponseEvent event);
+    }
+
+    public void addErrorSubscribe(String key, Event event) {
+        errorSubscribes.put(key, event);
+    }
+
+    public void addOkSubscribe(String key, Event event) {
+        okSubscribes.put(key, event);
+    }
+
+    public Event getErrorSubscribe(String key) {
+        return errorSubscribes.get(key);
+    }
+
+    public Event getOkSubscribe(String key) {
+        return okSubscribes.get(key);
+    }
+
+    public int getErrorSubscribesSize(){
+        return errorSubscribes.size();
+    }
+    public int getOkSubscribesSize(){
+        return okSubscribes.size();
+    }
+}

+ 31 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/alarm/AlarmEvent.java

@@ -0,0 +1,31 @@
+package com.zd.alg.iot.vmp.gb28181.event.alarm;
+
+import com.zd.alg.iot.vmp.gb28181.bean.DeviceAlarm;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @description: 报警事件
+ * @author: lawrencehj
+ * @data: 2021-01-20
+ */
+
+public class AlarmEvent extends ApplicationEvent {
+    /**
+     *
+     */
+    private static final long serialVersionUID = 1L;
+
+    public AlarmEvent(Object source) {
+        super(source);
+    }
+
+    private DeviceAlarm deviceAlarm;
+
+    public DeviceAlarm getAlarmInfo() {
+        return deviceAlarm;
+    }
+
+    public void setAlarmInfo(DeviceAlarm deviceAlarm) {
+        this.deviceAlarm = deviceAlarm;
+    }
+}

+ 58 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/alarm/AlarmEventListener.java

@@ -0,0 +1,58 @@
+package com.zd.alg.iot.vmp.gb28181.event.alarm;
+
+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.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @description: 报警事件监听
+ * @author: lawrencehj
+ * @data: 2021-01-20
+ */
+
+@Component
+public class AlarmEventListener implements ApplicationListener<AlarmEvent> {
+
+    private final static Logger logger = LoggerFactory.getLogger(AlarmEventListener.class);
+
+    private static Map<String, SseEmitter> sseEmitters = new Hashtable<>();
+
+    public void addSseEmitters(String browserId, SseEmitter sseEmitter) {
+        sseEmitters.put(browserId, sseEmitter);
+    }
+
+    @Override
+    public void onApplicationEvent(AlarmEvent event) {
+        if (logger.isDebugEnabled()) {
+            logger.debug("设备报警事件触发,deviceId:" + event.getAlarmInfo().getDeviceId() + ", "
+                    + event.getAlarmInfo().getAlarmDescription());
+        }
+        String msg = "<strong>设备编码:</strong> <i>" + event.getAlarmInfo().getDeviceId() + "</i>"
+                    + "<br><strong>报警描述:</strong> <i>" + event.getAlarmInfo().getAlarmDescription() + "</i>"
+                    + "<br><strong>报警时间:</strong> <i>" + event.getAlarmInfo().getAlarmTime() + "</i>"
+                    + "<br><strong>报警位置:</strong> <i>" + event.getAlarmInfo().getLongitude() + "</i>"
+                    + ", <i>" + event.getAlarmInfo().getLatitude() + "</i>";
+
+        for (Iterator<Map.Entry<String, SseEmitter>> it = sseEmitters.entrySet().iterator(); it.hasNext();) {
+            Map.Entry<String, SseEmitter> emitter = it.next();
+            logger.info("推送到SSE连接,浏览器ID: " + emitter.getKey());
+            try {
+                emitter.getValue().send(msg);
+            } catch (IOException | IllegalStateException e) {
+                if (logger.isDebugEnabled()) {
+                    logger.debug("SSE连接已关闭");
+                }
+                // 移除已关闭的连接
+                it.remove();
+                // e.printStackTrace();
+            }
+        }
+    }
+}

+ 61 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/offline/KeepaliveTimeoutListenerForPlatform.java

@@ -0,0 +1,61 @@
+package com.zd.alg.iot.vmp.gb28181.event.offline;
+
+import com.zd.alg.iot.vmp.gb28181.event.EventPublisher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+import org.springframework.stereotype.Component;
+
+import com.zd.alg.iot.vmp.common.VideoManagerConstants;
+
+/**
+ * @Description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
+ * @author: dgs
+ * @date:   2020年5月6日 上午11:35:46
+ */
+@Component
+public class KeepaliveTimeoutListenerForPlatform extends KeyExpirationEventMessageListener {
+
+    private Logger logger = LoggerFactory.getLogger(KeepaliveTimeoutListenerForPlatform.class);
+
+	@Autowired
+	private EventPublisher publisher;
+
+    public KeepaliveTimeoutListenerForPlatform(RedisMessageListenerContainer listenerContainer) {
+        super(listenerContainer);
+    }
+
+
+    /**
+     * 监听失效的key
+     * @param message
+     * @param pattern
+     */
+    @Override
+    public void onMessage(Message message, byte[] pattern) {
+        //  获取失效的key
+        String expiredKey = message.toString();
+        logger.debug(expiredKey);
+        if(!expiredKey.startsWith(VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX)){
+        	logger.debug("收到redis过期监听,但开头不是"+VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX+",忽略");
+        	return;
+        }
+        // 平台心跳到期,需要重发, 判断是否已经多次未收到心跳回复, 多次未收到,则重新发起注册, 注册尝试多次未得到回复,则认为平台离线
+        if (expiredKey.startsWith(VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX)) {
+            String platformGBId = expiredKey.substring(VideoManagerConstants.PLATFORM_KEEPLIVEKEY_PREFIX.length(),expiredKey.length());
+
+            publisher.platformKeepaliveExpireEventPublish(platformGBId);
+        }else if (expiredKey.startsWith(VideoManagerConstants.PLATFORM_REGISTER_PREFIX)) {
+            String platformGBId = expiredKey.substring(VideoManagerConstants.PLATFORM_REGISTER_PREFIX.length(),expiredKey.length());
+
+            publisher.platformNotRegisterEventPublish(platformGBId);
+        }else{
+            String deviceId = expiredKey.substring(VideoManagerConstants.KEEPLIVEKEY_PREFIX.length(),expiredKey.length());
+            publisher.outlineEventPublish(deviceId, VideoManagerConstants.EVENT_OUTLINE_TIMEOUT);
+        }
+
+    }
+}

+ 48 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/offline/KeepliveTimeoutListener.java

@@ -0,0 +1,48 @@
+package com.zd.alg.iot.vmp.gb28181.event.offline;
+
+import com.zd.alg.iot.vmp.gb28181.event.EventPublisher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+import org.springframework.stereotype.Component;
+
+import com.zd.alg.iot.vmp.common.VideoManagerConstants;
+
+/**
+ * @Description:设备心跳超时监听,借助redis过期特性,进行监听,监听到说明设备心跳超时,发送离线事件
+ * @author: dgs
+ * @date:   2020年5月6日 上午11:35:46
+ */
+@Component
+public class KeepliveTimeoutListener extends KeyExpirationEventMessageListener {
+
+    private Logger logger = LoggerFactory.getLogger(KeepliveTimeoutListener.class);
+
+	@Autowired
+	private EventPublisher publisher;
+
+	public KeepliveTimeoutListener(RedisMessageListenerContainer listenerContainer) {
+		super(listenerContainer);
+	}
+
+	/**
+     * 监听失效的key,key格式为keeplive_deviceId
+     * @param message
+     * @param pattern
+     */
+    @Override
+    public void onMessage(Message message, byte[] pattern) {
+        //  获取失效的key
+        String expiredKey = message.toString();
+        if(!expiredKey.startsWith(VideoManagerConstants.KEEPLIVEKEY_PREFIX)){
+        	logger.debug("收到redis过期监听,但开头不是"+VideoManagerConstants.KEEPLIVEKEY_PREFIX+",忽略");
+        	return;
+        }
+
+        String deviceId = expiredKey.substring(VideoManagerConstants.KEEPLIVEKEY_PREFIX.length(),expiredKey.length());
+        publisher.outlineEventPublish(deviceId, VideoManagerConstants.EVENT_OUTLINE_TIMEOUT);
+    }
+}

+ 40 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/offline/OfflineEvent.java

@@ -0,0 +1,40 @@
+package com.zd.alg.iot.vmp.gb28181.event.offline;
+
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @Description: 离线事件类
+ * @author: dgs
+ * @date:   2020年5月6日 上午11:33:13
+ */
+public class OfflineEvent extends ApplicationEvent {
+
+	/**
+	 *
+	 */
+	private static final long serialVersionUID = 1L;
+
+	public OfflineEvent(Object source) {
+		super(source);
+	}
+
+	private String deviceId;
+
+	private String from;
+
+	public String getDeviceId() {
+		return deviceId;
+	}
+
+	public void setDeviceId(String deviceId) {
+		this.deviceId = deviceId;
+	}
+
+	public String getFrom() {
+		return from;
+	}
+
+	public void setFrom(String from) {
+		this.from = from;
+	}
+}

+ 66 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/offline/OfflineEventListener.java

@@ -0,0 +1,66 @@
+package com.zd.alg.iot.vmp.gb28181.event.offline;
+
+import com.zd.alg.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor;
+import com.zd.alg.iot.vmp.storager.IVideoManagerStorager;
+import com.zd.common.core.enums.HardwareOperate;
+import com.zd.alg.iot.vmp.common.VideoManagerConstants;
+import com.zd.system.api.laboratory.RemoteLaboratoryService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+import com.zd.alg.iot.vmp.utils.redis.RedisUtil;
+
+/**
+ * @Description: 离线事件监听器,监听到离线后,修改设备离在线状态。 设备离线有两个来源:
+ *               1、设备主动注销,发送注销指令,{@link RegisterRequestProcessor}
+ *               2、设备未知原因离线,心跳超时,{@link OfflineEventListener}
+ * @author: dgs
+ * @date: 2020年5月6日 下午1:51:23
+ */
+@Component
+public class OfflineEventListener implements ApplicationListener<OfflineEvent> {
+
+	private final static Logger logger = LoggerFactory.getLogger(OfflineEventListener.class);
+
+	@Autowired
+	private RemoteLaboratoryService remoteLaboratoryService;
+
+	@Autowired
+	private IVideoManagerStorager storager;
+
+	@Autowired
+    private RedisUtil redis;
+
+	@Override
+	public void onApplicationEvent(OfflineEvent event) {
+
+		if (logger.isInfoEnabled()) {
+			logger.info("设备离线事件触发,deviceId:" + event.getDeviceId() + ",from:" + event.getFrom());
+		}
+
+		String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + event.getDeviceId();
+
+		switch (event.getFrom()) {
+			// 心跳超时触发的离线事件,说明redis中已删除,无需处理
+			case VideoManagerConstants.EVENT_OUTLINE_TIMEOUT:
+				break;
+			// 设备主动注销触发的离线事件,需要删除redis中的超时监听
+			case VideoManagerConstants.EVENT_OUTLINE_UNREGISTER:
+				redis.del(key);
+				break;
+			default:
+				boolean exist = redis.hasKey(key);
+				if (exist) {
+					redis.del(key);
+				}
+		}
+
+		// 处理离线监听
+		storager.outline(event.getDeviceId());
+		//mqtt离线推送
+        remoteLaboratoryService.sends(event.getDeviceId(), HardwareOperate.OFFLINE);
+	}
+}

+ 42 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/online/OnlineEvent.java

@@ -0,0 +1,42 @@
+package com.zd.alg.iot.vmp.gb28181.event.online;
+
+import com.zd.alg.iot.vmp.gb28181.bean.Device;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @Description: 在线事件类
+ * @author: dgs
+ * @date:   2020年5月6日 上午11:32:56
+ */
+public class OnlineEvent extends ApplicationEvent {
+
+	/**
+	 *
+	 */
+	private static final long serialVersionUID = 1L;
+
+	public OnlineEvent(Object source) {
+		super(source);
+	}
+
+	private Device device;
+
+	private String from;
+
+	public Device getDevice() {
+		return device;
+	}
+
+	public void setDevice(Device device) {
+		this.device = device;
+	}
+
+	public String getFrom() {
+		return from;
+	}
+
+	public void setFrom(String from) {
+		this.from = from;
+	}
+
+}

+ 85 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/online/OnlineEventListener.java

@@ -0,0 +1,85 @@
+package com.zd.alg.iot.vmp.gb28181.event.online;
+
+import com.zd.alg.iot.vmp.conf.SipConfig;
+import com.zd.alg.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor;
+import com.zd.alg.iot.vmp.gb28181.transmit.request.impl.RegisterRequestProcessor;
+import com.zd.alg.iot.vmp.storager.IVideoManagerStorager;
+import com.zd.common.core.enums.HardwareOperate;
+import com.zd.alg.iot.vmp.gb28181.bean.Device;
+import com.zd.alg.iot.vmp.common.VideoManagerConstants;
+import com.zd.alg.iot.vmp.utils.redis.RedisUtil;
+import com.zd.system.api.laboratory.RemoteLaboratoryService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+import java.text.SimpleDateFormat;
+
+/**
+ * @Description: 在线事件监听器,监听到离线后,修改设备离在线状态。 设备在线有两个来源:
+ *               1、设备主动注销,发送注销指令,{@link RegisterRequestProcessor}
+ *               2、设备未知原因离线,心跳超时,{@link MessageRequestProcessor}
+ * @author: dgs
+ * @date: 2020年5月6日 下午1:51:23
+ */
+@Component
+public class OnlineEventListener implements ApplicationListener<OnlineEvent> {
+
+	private final static Logger logger = LoggerFactory.getLogger(OnlineEventListener.class);
+
+	@Autowired
+	private IVideoManagerStorager storager;
+
+	@Autowired
+    private RedisUtil redis;
+
+	@Autowired
+    private SipConfig sipConfig;
+
+	private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+	@Autowired
+	private RemoteLaboratoryService remoteLaboratoryService;
+
+	@Override
+	public void onApplicationEvent(OnlineEvent event) {
+
+		if (logger.isInfoEnabled()) {
+			logger.info("设备上线事件触发,deviceId:" + event.getDevice().getDeviceId() + ",from:" + event.getFrom());
+		}
+		Device device = event.getDevice();
+		String key = VideoManagerConstants.KEEPLIVEKEY_PREFIX + event.getDevice().getDeviceId();
+
+		switch (event.getFrom()) {
+		// 注册时触发的在线事件,先在redis中增加超时超时监听
+		case VideoManagerConstants.EVENT_ONLINE_REGISTER:
+			// 超时时间
+			redis.set(key, event.getDevice().getDeviceId(), sipConfig.getKeepaliveTimeOut());
+			device.setRegisterTime(format.format(System.currentTimeMillis()));
+			break;
+		// 设备主动发送心跳触发的在线事件
+		case VideoManagerConstants.EVENT_ONLINE_KEEPLIVE:
+			boolean exist = redis.hasKey(key);
+			// 先判断是否还存在,当设备先心跳超时后又发送心跳时,redis没有监听,需要增加
+			if (!exist) {
+				redis.set(key, event.getDevice().getDeviceId(), sipConfig.getKeepaliveTimeOut());
+			} else {
+				redis.expire(key, sipConfig.getKeepaliveTimeOut());
+			}
+			device.setKeepaliveTime(format.format(System.currentTimeMillis()));
+			break;
+		// 设备主动发送消息触发的在线事件
+		case VideoManagerConstants.EVENT_ONLINE_MESSAGE:
+
+			break;
+		}
+
+		device.setOnline(1);
+		logger.info("***************************处理时间监听,监听上线******************************");
+		// 处理上线监听
+		storager.updateDevice(device);
+		remoteLaboratoryService.sends(device.getDeviceId(), HardwareOperate.ONLINE);
+	}
+}

+ 28 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEvent.java

@@ -0,0 +1,28 @@
+package com.zd.alg.iot.vmp.gb28181.event.platformKeepaliveExpire;
+
+import org.springframework.context.ApplicationEvent;
+
+/**
+ *  平台心跳超时事件
+ */
+public class PlatformKeepaliveExpireEvent extends ApplicationEvent {
+
+    /**
+     * Add default serial version ID
+     */
+    private static final long serialVersionUID = 1L;
+
+    private String platformGbID;
+
+    public PlatformKeepaliveExpireEvent(Object source) {
+        super(source);
+    }
+
+    public String getPlatformGbID() {
+        return platformGbID;
+    }
+
+    public void setPlatformGbID(String platformGbID) {
+        this.platformGbID = platformGbID;
+    }
+}

+ 87 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/platformKeepaliveExpire/PlatformKeepaliveExpireEventLister.java

@@ -0,0 +1,87 @@
+package com.zd.alg.iot.vmp.gb28181.event.platformKeepaliveExpire;
+
+import com.zd.alg.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
+import com.zd.alg.iot.vmp.storager.IRedisCatchStorage;
+import com.zd.alg.iot.vmp.storager.IVideoManagerStorager;
+import com.zd.alg.iot.vmp.gb28181.bean.ParentPlatform;
+import com.zd.alg.iot.vmp.gb28181.bean.ParentPlatformCatch;
+import com.zd.alg.iot.vmp.gb28181.event.EventPublisher;
+import com.zd.alg.iot.vmp.gb28181.event.SipSubscribe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+import javax.sip.ResponseEvent;
+import javax.sip.message.Response;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @Description: 平台心跳超时事件
+ * @author: panll
+ * @date: 2020年11月5日 10:00
+ */
+@Component
+public class PlatformKeepaliveExpireEventLister implements ApplicationListener<PlatformKeepaliveExpireEvent> {
+
+
+    private final static Logger logger = LoggerFactory.getLogger(PlatformKeepaliveExpireEventLister.class);
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private ISIPCommanderForPlatform sipCommanderForPlatform;
+
+    @Autowired
+    private SipSubscribe sipSubscribe;
+
+    @Autowired
+    private EventPublisher publisher;
+
+    @Override
+    public void onApplicationEvent(@NotNull PlatformKeepaliveExpireEvent event) {
+
+        if (logger.isInfoEnabled()) {
+            logger.info("平台心跳到期事件事件触发,平台国标ID:" + event.getPlatformGbID());
+        }
+        ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformGbID());
+        ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(event.getPlatformGbID());
+        if (parentPlatformCatch == null) {
+            return;
+        }
+        if (parentPlatform == null) {
+            logger.info("平台心跳到期事件事件触发,但平台已经删除!!! 平台国标ID:" + event.getPlatformGbID());
+            return;
+        }
+        parentPlatformCatch.setParentPlatform(parentPlatform);
+        // 发送心跳
+        if (parentPlatformCatch.getKeepAliveReply() >= 3) {
+            // 有3次未收到心跳回复, 设置平台状态为离线, 开始重新注册
+            logger.info("有3次未收到心跳回复,标记设置平台状态为离线, 并重新注册 平台国标ID:" + event.getPlatformGbID());
+            storager.updateParentPlatformStatus(event.getPlatformGbID(), false);
+            publisher.platformNotRegisterEventPublish(event.getPlatformGbID());
+            parentPlatformCatch.setKeepAliveReply(0);
+        }else {
+            // 再次发送心跳
+            String callId = sipCommanderForPlatform.keepalive(parentPlatform);
+
+            parentPlatformCatch.setKeepAliveReply( parentPlatformCatch.getKeepAliveReply() + 1);
+            // 存储心跳信息, 并设置状态为未回复, 如果多次过期仍未收到回复,则认为上级平台已经离线
+            redisCatchStorage.updatePlatformKeepalive(parentPlatform);
+            redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
+
+            sipSubscribe.addOkSubscribe(callId, (ResponseEvent responseEvent) ->{
+                if (responseEvent.getResponse().getStatusCode() == Response.OK) {
+                    // 收到心跳响应信息,
+                    parentPlatformCatch.setKeepAliveReply(0);
+                    redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
+                }
+            } );
+        }
+    }
+}

+ 25 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEvent.java

@@ -0,0 +1,25 @@
+package com.zd.alg.iot.vmp.gb28181.event.platformNotRegister;
+
+import org.springframework.context.ApplicationEvent;
+
+public class PlatformNotRegisterEvent extends ApplicationEvent {
+
+    /**
+     * Add default serial version ID
+     */
+    private static final long serialVersionUID = 1L;
+
+    private String platformGbID;
+
+    public PlatformNotRegisterEvent(Object source) {
+        super(source);
+    }
+
+    public String getPlatformGbID() {
+        return platformGbID;
+    }
+
+    public void setPlatformGbID(String platformGbID) {
+        this.platformGbID = platformGbID;
+    }
+}

+ 105 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/event/platformNotRegister/PlatformNotRegisterEventLister.java

@@ -0,0 +1,105 @@
+package com.zd.alg.iot.vmp.gb28181.event.platformNotRegister;
+
+import com.zd.alg.iot.vmp.conf.SipConfig;
+import com.zd.alg.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
+import com.zd.alg.iot.vmp.service.IMediaServerService;
+import com.zd.alg.iot.vmp.storager.IRedisCatchStorage;
+import com.zd.alg.iot.vmp.storager.IVideoManagerStorager;
+import com.zd.alg.iot.vmp.gb28181.bean.ParentPlatform;
+import com.zd.alg.iot.vmp.gb28181.bean.SendRtpItem;
+import com.zd.alg.iot.vmp.gb28181.event.SipSubscribe;
+import com.zd.alg.iot.vmp.media.zlm.ZLMRTPServerFactory;
+import com.zd.alg.iot.vmp.media.zlm.dto.MediaServerItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * @Description: 平台未注册事件,来源有二:
+ *               1、平台新添加
+ *               2、平台心跳超时
+ * @author: panll
+ * @date: 2020年11月24日 10:00
+ */
+@Component
+public class PlatformNotRegisterEventLister implements ApplicationListener<PlatformNotRegisterEvent> {
+
+    private final static Logger logger = LoggerFactory.getLogger(PlatformNotRegisterEventLister.class);
+
+    @Autowired
+    private IVideoManagerStorager storager;
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+    @Autowired
+    private IMediaServerService mediaServerService;
+
+    @Autowired
+    private SIPCommanderFroPlatform sipCommanderFroPlatform;
+
+    @Autowired
+    private ZLMRTPServerFactory zlmrtpServerFactory;
+
+    @Autowired
+    private SipConfig config;
+
+    // @Autowired
+    // private RedisUtil redis;
+
+    @Override
+    public void onApplicationEvent(PlatformNotRegisterEvent event) {
+
+        logger.info("平台未注册事件触发,平台国标ID:" + event.getPlatformGbID());
+
+        ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(event.getPlatformGbID());
+        if (parentPlatform == null) {
+            logger.info("平台未注册事件触发,但平台已经删除!!! 平台国标ID:" + event.getPlatformGbID());
+            return;
+        }
+        // 查询是否有推流, 如果有则都停止
+        List<SendRtpItem> sendRtpItems = redisCatchStorage.querySendRTPServer(event.getPlatformGbID());
+        logger.info("停止[ {} ]的所有推流size", sendRtpItems.size());
+        if (sendRtpItems != null && sendRtpItems.size() > 0) {
+            logger.info("停止[ {} ]的所有推流", event.getPlatformGbID());
+            StringBuilder app = new StringBuilder();
+            StringBuilder stream = new StringBuilder();
+            for (SendRtpItem sendRtpItem : sendRtpItems) {
+                if (app.length() != 0) {
+                    app.append(",");
+                }
+                app.append(sendRtpItem.getApp());
+                if (stream.length() != 0) {
+                    stream.append(",");
+                }
+                stream.append(sendRtpItem.getStreamId());
+                redisCatchStorage.deleteSendRTPServer(event.getPlatformGbID(), sendRtpItem.getChannelId());
+                MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+                Map<String, Object> param = new HashMap<>();
+                param.put("vhost", "__defaultVhost__");
+                param.put("app", app.toString());
+                param.put("stream", stream.toString());
+                zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
+            }
+
+
+        }
+
+        Timer timer = new Timer();
+        SipSubscribe.Event okEvent = (responseEvent)->{
+            timer.cancel();
+        };
+        logger.info("向平台注册,平台国标ID:" + event.getPlatformGbID());
+        sipCommanderFroPlatform.register(parentPlatform, null, okEvent);
+        // 设置注册失败则每隔15秒发起一次注册
+        timer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+                logger.info("再次向平台注册,平台国标ID:" + event.getPlatformGbID());
+                sipCommanderFroPlatform.register(parentPlatform, null, okEvent);
+            }
+        }, config.getRegisterTimeInterval(), config.getRegisterTimeInterval());//十五秒后再次发起注册
+    }
+}

+ 140 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/session/SsrcConfig.java

@@ -0,0 +1,140 @@
+package com.zd.alg.iot.vmp.gb28181.session;
+
+import com.zd.alg.iot.vmp.utils.ConfigConst;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+
+public class SsrcConfig {
+
+    /**
+     * zlm流媒体服务器Id
+     */
+    private String mediaServerId;
+
+    private String ssrcPrefix;
+    /**
+     * zlm流媒体服务器已用会话句柄
+     */
+    private List<String> isUsed;
+    /**
+     * zlm流媒体服务器可用会话句柄
+     */
+    private List<String> notUsed;
+
+    public SsrcConfig() {
+    }
+
+    public SsrcConfig(String mediaServerId, Set<String> usedSet, String sipDomain) {
+        this.mediaServerId = mediaServerId;
+        this.isUsed = new ArrayList<>();
+        this.ssrcPrefix = sipDomain.substring(3, 8);
+        this.notUsed = new ArrayList<>();
+        for (int i = 1; i < ConfigConst.MAX_STRTEAM_COUNT; i++) {
+            String ssrc;
+            if (i < 10) {
+                ssrc = "000" + i;
+            } else if (i < 100) {
+                ssrc = "00" + i;
+            } else if (i < 1000) {
+                ssrc = "0" + i;
+            } else {
+                ssrc = String.valueOf(i);
+            }
+            if (null == usedSet || !usedSet.contains(ssrc)) {
+                this.notUsed.add(ssrc);
+            } else {
+                this.isUsed.add(ssrc);
+            }
+        }
+    }
+
+
+    /**
+     * 获取视频预览的SSRC值,第一位固定为0
+     * @return ssrc
+     */
+    public String getPlaySsrc() {
+        return "0" + getSsrcPrefix() + getSN();
+    }
+
+    /**
+     * 获取录像回放的SSRC值,第一位固定为1
+     *
+     */
+    public String getPlayBackSsrc() {
+        return "1" + getSsrcPrefix() + getSN();
+    }
+
+    /**
+     * 释放ssrc,主要用完的ssrc一定要释放,否则会耗尽
+     * @param ssrc 需要重置的ssrc
+     */
+    public void releaseSsrc(String ssrc) {
+        if (ssrc == null) {
+            return;
+        }
+        String sn = ssrc.substring(6);
+        try {
+            isUsed.remove(sn);
+            notUsed.add(sn);
+        }catch (NullPointerException e){
+            System.out.printf("11111");
+        }
+    }
+
+    /**
+     * 获取后四位数SN,随机数
+     *
+     */
+    private String getSN() {
+        String sn = null;
+        int index = 0;
+        if (notUsed.size() == 0) {
+            throw new RuntimeException("ssrc已经用完");
+        } else if (notUsed.size() == 1) {
+            sn = notUsed.get(0);
+        } else {
+            index = new Random().nextInt(notUsed.size() - 1);
+            sn = notUsed.get(index);
+        }
+        notUsed.remove(index);
+        isUsed.add(sn);
+        return sn;
+    }
+
+    public String getSsrcPrefix() {
+        return ssrcPrefix;
+    }
+
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+
+    public void setSsrcPrefix(String ssrcPrefix) {
+        this.ssrcPrefix = ssrcPrefix;
+    }
+
+    public List<String> getIsUsed() {
+        return isUsed;
+    }
+
+    public void setIsUsed(List<String> isUsed) {
+        this.isUsed = isUsed;
+    }
+
+    public List<String> getNotUsed() {
+        return notUsed;
+    }
+
+    public void setNotUsed(List<String> notUsed) {
+        this.notUsed = notUsed;
+    }
+
+}

+ 106 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/session/VideoStreamSessionManager.java

@@ -0,0 +1,106 @@
+package com.zd.alg.iot.vmp.gb28181.session;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.sip.ClientTransaction;
+import javax.sip.Dialog;
+
+import com.zd.alg.iot.vmp.common.VideoManagerConstants;
+import com.zd.alg.iot.vmp.gb28181.bean.SsrcTransaction;
+import com.zd.alg.iot.vmp.utils.SerializeUtils;
+import com.zd.alg.iot.vmp.utils.redis.RedisUtil;
+import gov.nist.javax.sip.stack.SIPDialog;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Description:视频流session管理器,管理视频预览、预览回放的通信句柄
+ * @author: dgs
+ * @date:   2021年10月13日 下午4:03:02
+ */
+@Component
+public class VideoStreamSessionManager {
+
+	@Autowired
+	private RedisUtil redisUtil;
+
+	public void put(String deviceId, String channelId ,String ssrc, String streamId, String mediaServerId, ClientTransaction transaction){
+		SsrcTransaction ssrcTransaction = new SsrcTransaction();
+		ssrcTransaction.setDeviceId(deviceId);
+		ssrcTransaction.setChannelId(channelId);
+		ssrcTransaction.setStreamId(streamId);
+		byte[] transactionByteArray = SerializeUtils.serialize(transaction);
+		ssrcTransaction.setTransaction(transactionByteArray);
+		ssrcTransaction.setSsrc(ssrc);
+		ssrcTransaction.setMediaServerId(mediaServerId);
+
+		redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX +  deviceId + "_" + channelId, ssrcTransaction);
+	}
+
+	public void put(String deviceId, String channelId , Dialog dialog){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+		if (ssrcTransaction != null) {
+			byte[] dialogByteArray = SerializeUtils.serialize(dialog);
+			ssrcTransaction.setDialog(dialogByteArray);
+		}
+		redisUtil.set(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX +  deviceId + "_" + channelId, ssrcTransaction);
+	}
+
+
+	public ClientTransaction getTransaction(String deviceId, String channelId){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+		if (ssrcTransaction == null) return null;
+		byte[] transactionByteArray = ssrcTransaction.getTransaction();
+		ClientTransaction clientTransaction = (ClientTransaction)SerializeUtils.deSerialize(transactionByteArray);
+		return clientTransaction;
+	}
+
+	public SIPDialog getDialog(String deviceId, String channelId){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+		if (ssrcTransaction == null) return null;
+		byte[] dialogByteArray = ssrcTransaction.getDialog();
+		if (dialogByteArray == null) return null;
+		SIPDialog dialog = (SIPDialog)SerializeUtils.deSerialize(dialogByteArray);
+		return dialog;
+	}
+
+	public SsrcTransaction getSsrcTransaction(String deviceId, String channelId){
+		SsrcTransaction ssrcTransaction = (SsrcTransaction)redisUtil.get(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX + deviceId + "_" + channelId);
+		return ssrcTransaction;
+	}
+
+	public String getStreamId(String deviceId, String channelId){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+		if (ssrcTransaction == null) return null;
+		return ssrcTransaction.getStreamId();
+	}
+	public String getMediaServerId(String deviceId, String channelId){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+		if (ssrcTransaction == null) return null;
+		return ssrcTransaction.getMediaServerId();
+	}
+
+	public String getSSRC(String deviceId, String channelId){
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+		if (ssrcTransaction == null) return null;
+		return ssrcTransaction.getSsrc();
+	}
+
+	public void remove(String deviceId, String channelId) {
+		SsrcTransaction ssrcTransaction = getSsrcTransaction(deviceId, channelId);
+		if (ssrcTransaction == null) return;
+		redisUtil.del(VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX +  deviceId + "_" + channelId);
+	}
+
+	public List<SsrcTransaction> getAllSsrc() {
+		List<Object> ssrcTransactionKeys = redisUtil.scan(String.format("%s_*_*", VideoManagerConstants.MEDIA_TRANSACTION_USED_PREFIX));
+		List<SsrcTransaction> result= new ArrayList<>();
+		for (int i = 0; i < ssrcTransactionKeys.size(); i++) {
+			String key = (String)ssrcTransactionKeys.get(i);
+			SsrcTransaction ssrcTransaction = (SsrcTransaction)redisUtil.get(key);
+			result.add(ssrcTransaction);
+		}
+		return result;
+	}
+}

+ 230 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/SIPProcessorFactory.java

@@ -0,0 +1,230 @@
+package com.zd.alg.iot.vmp.gb28181.transmit;
+
+import com.zd.alg.iot.vmp.conf.SipConfig;
+import com.zd.alg.iot.vmp.gb28181.transmit.request.impl.*;
+import com.zd.alg.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
+import com.zd.alg.iot.vmp.gb28181.transmit.response.impl.*;
+import com.zd.alg.iot.vmp.service.IDeviceAlarmService;
+import com.zd.alg.iot.vmp.service.IMediaServerService;
+import com.zd.alg.iot.vmp.service.IPlayService;
+import com.zd.alg.iot.vmp.storager.IRedisCatchStorage;
+import com.zd.alg.iot.vmp.storager.IVideoManagerStorager;
+import com.zd.alg.iot.vmp.gb28181.auth.RegisterLogicHandler;
+import com.zd.alg.iot.vmp.gb28181.event.DeviceOffLineDetector;
+import com.zd.alg.iot.vmp.gb28181.event.EventPublisher;
+import com.zd.alg.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.zd.alg.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import com.zd.alg.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
+import com.zd.alg.iot.vmp.gb28181.transmit.request.ISIPRequestProcessor;
+import com.zd.alg.iot.vmp.media.zlm.ZLMRTPServerFactory;
+import com.zd.alg.iot.vmp.utils.SpringBeanFactory;
+import com.zd.alg.iot.vmp.utils.redis.RedisUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Component;
+
+import javax.sip.RequestEvent;
+import javax.sip.ResponseEvent;
+import javax.sip.SipProvider;
+import javax.sip.header.CSeqHeader;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+
+/**
+ * @Description: SIP信令处理分配
+ * @author: dgs
+ * @date:   2020年5月3日 下午4:24:37
+ */
+@Component
+public class SIPProcessorFactory {
+
+	// private final static Logger logger = LoggerFactory.getLogger(SIPProcessorFactory.class);
+
+	@Autowired
+	private SipConfig sipConfig;
+
+	@Autowired
+	private RegisterLogicHandler handler;
+
+	@Autowired
+	private IVideoManagerStorager storager;
+
+	@Autowired
+	private IRedisCatchStorage redisCatchStorage;
+
+	@Autowired
+	private EventPublisher publisher;
+
+	@Autowired
+	private SIPCommander cmder;
+
+	@Autowired
+	private SIPCommanderFroPlatform cmderFroPlatform;
+
+	@Autowired
+	private IDeviceAlarmService deviceAlarmService;
+
+	@Autowired
+	private RedisUtil redis;
+
+	@Autowired
+	private DeferredResultHolder deferredResultHolder;
+
+	@Autowired
+	private DeviceOffLineDetector offLineDetector;
+
+	@Autowired
+	private InviteResponseProcessor inviteResponseProcessor;
+
+	@Autowired
+	private ByeResponseProcessor byeResponseProcessor;
+
+	@Autowired
+	private CancelResponseProcessor cancelResponseProcessor;
+
+	@Autowired
+	@Lazy
+	private RegisterResponseProcessor registerResponseProcessor;
+
+
+	@Autowired
+	private OtherResponseProcessor otherResponseProcessor;
+
+	@Autowired
+	private IPlayService playService;
+
+	@Autowired
+	private ZLMRTPServerFactory zlmrtpServerFactory;
+
+	@Autowired
+	private IMediaServerService mediaServerService;
+
+	// 注:这里使用注解会导致循环依赖注入,暂用springBean
+	private SipProvider tcpSipProvider;
+
+	// 注:这里使用注解会导致循环依赖注入,暂用springBean
+	private SipProvider udpSipProvider;
+
+	public ISIPRequestProcessor createRequestProcessor(RequestEvent evt) {
+		Request request = evt.getRequest();
+		String method = request.getMethod();
+//		logger.info("接收到消息:"+request.getMethod());
+//		sipSubscribe.getSubscribe(evt.getServerTransaction().getBranchId()).response(evt);
+		if (Request.INVITE.equals(method)) {
+			InviteRequestProcessor processor = new InviteRequestProcessor();
+			processor.setRequestEvent(evt);
+			processor.setTcpSipProvider(getTcpSipProvider());
+			processor.setUdpSipProvider(getUdpSipProvider());
+
+			processor.setCmder(cmder);
+			processor.setCmderFroPlatform(cmderFroPlatform);
+			processor.setPlayService(playService);
+			processor.setStorager(storager);
+			processor.setRedisCatchStorage(redisCatchStorage);
+			processor.setZlmrtpServerFactory(zlmrtpServerFactory);
+			processor.setMediaServerService(mediaServerService);
+			return processor;
+		} else if (Request.REGISTER.equals(method)) {
+			RegisterRequestProcessor processor = new RegisterRequestProcessor();
+			processor.setRequestEvent(evt);
+			processor.setTcpSipProvider(getTcpSipProvider());
+			processor.setUdpSipProvider(getUdpSipProvider());
+			processor.setHandler(handler);
+			processor.setPublisher(publisher);
+			processor.setSipConfig(sipConfig);
+			processor.setVideoManagerStorager(storager);
+			return processor;
+		} else if (Request.SUBSCRIBE.equals(method)) {
+			SubscribeRequestProcessor processor = new SubscribeRequestProcessor();
+			processor.setTcpSipProvider(getTcpSipProvider());
+			processor.setUdpSipProvider(getUdpSipProvider());
+			processor.setRequestEvent(evt);
+			return processor;
+		} else if (Request.ACK.equals(method)) {
+			AckRequestProcessor processor = new AckRequestProcessor();
+			processor.setRequestEvent(evt);
+			processor.setRedisCatchStorage(redisCatchStorage);
+			processor.setZlmrtpServerFactory(zlmrtpServerFactory);
+			processor.setMediaServerService(mediaServerService);
+			return processor;
+		} else if (Request.BYE.equals(method)) {
+			ByeRequestProcessor processor = new ByeRequestProcessor();
+			processor.setRequestEvent(evt);
+			processor.setRedisCatchStorage(redisCatchStorage);
+			processor.setStorager(storager);
+			processor.setZlmrtpServerFactory(zlmrtpServerFactory);
+			processor.setSIPCommander(cmder);
+			processor.setMediaServerService(mediaServerService);
+			return processor;
+		} else if (Request.CANCEL.equals(method)) {
+			CancelRequestProcessor processor = new CancelRequestProcessor();
+			processor.setRequestEvent(evt);
+			return processor;
+		} else if (Request.MESSAGE.equals(method)) {
+			MessageRequestProcessor processor = new MessageRequestProcessor();
+			processor.setRequestEvent(evt);
+			processor.setTcpSipProvider(getTcpSipProvider());
+			processor.setUdpSipProvider(getUdpSipProvider());
+			processor.setPublisher(publisher);
+			processor.setRedis(redis);
+			processor.setDeferredResultHolder(deferredResultHolder);
+			processor.setOffLineDetector(offLineDetector);
+			processor.setCmder(cmder);
+			processor.setCmderFroPlatform(cmderFroPlatform);
+			processor.setDeviceAlarmService(deviceAlarmService);
+			processor.setStorager(storager);
+			processor.setRedisCatchStorage(redisCatchStorage);
+			return processor;
+		} else if (Request.NOTIFY.equalsIgnoreCase(method)) {
+			NotifyRequestProcessor processor = new NotifyRequestProcessor();
+			processor.setRequestEvent(evt);
+			processor.setTcpSipProvider(getTcpSipProvider());
+			processor.setUdpSipProvider(getUdpSipProvider());
+			processor.setPublisher(publisher);
+			processor.setRedis(redis);
+			processor.setDeferredResultHolder(deferredResultHolder);
+			processor.setOffLineDetector(offLineDetector);
+			processor.setCmder(cmder);
+			processor.setStorager(storager);
+			processor.setRedisCatchStorage(redisCatchStorage);
+			return processor;
+		} else {
+			OtherRequestProcessor processor = new OtherRequestProcessor();
+			processor.setRequestEvent(evt);
+			return processor;
+		}
+	}
+
+	public ISIPResponseProcessor createResponseProcessor(ResponseEvent evt) {
+
+		Response response = evt.getResponse();
+		CSeqHeader cseqHeader = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
+		String method = cseqHeader.getMethod();
+		if(Request.INVITE.equals(method)){
+			return inviteResponseProcessor;
+		} else if (Request.BYE.equals(method)) {
+			return byeResponseProcessor;
+		} else if (Request.CANCEL.equals(method)) {
+			return cancelResponseProcessor;
+		}else if (Request.REGISTER.equals(method)) {
+			return registerResponseProcessor;
+		} else {
+			return otherResponseProcessor;
+		}
+	}
+
+	private SipProvider getTcpSipProvider() {
+		if (tcpSipProvider == null) {
+			tcpSipProvider = (SipProvider) SpringBeanFactory.getBean("tcpSipProvider");
+		}
+		return tcpSipProvider;
+	}
+
+	private SipProvider getUdpSipProvider() {
+		if (udpSipProvider == null) {
+			udpSipProvider = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
+		}
+		return udpSipProvider;
+	}
+
+}

+ 78 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/callback/CheckForAllRecordsThread.java

@@ -0,0 +1,78 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.callback;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import com.zd.alg.iot.vmp.gb28181.bean.RecordInfo;
+import com.zd.alg.iot.vmp.gb28181.bean.RecordItem;
+import com.zd.alg.iot.vmp.gb28181.transmit.request.impl.MessageRequestProcessor;
+import com.zd.alg.iot.vmp.utils.redis.RedisUtil;
+
+import org.slf4j.Logger;
+
+@SuppressWarnings("unchecked")
+public class CheckForAllRecordsThread extends Thread {
+
+    private String key;
+
+    private RecordInfo recordInfo;
+
+    private RedisUtil redis;
+
+    private Logger logger;
+
+    private DeferredResultHolder deferredResultHolder;
+
+    public CheckForAllRecordsThread(String key, RecordInfo recordInfo) {
+        this.key = key;
+        this.recordInfo = recordInfo;
+    }
+
+    public void run() {
+
+        String cacheKey = this.key;
+
+        for (long stop = System.nanoTime() + TimeUnit.SECONDS.toNanos(10); stop > System.nanoTime();) {
+            List<Object> cacheKeys = redis.scan(cacheKey + "_*");
+            List<RecordItem> totalRecordList = new ArrayList<RecordItem>();
+            for (int i = 0; i < cacheKeys.size(); i++) {
+                totalRecordList.addAll((List<RecordItem>) redis.get(cacheKeys.get(i).toString()));
+            }
+            if (totalRecordList.size() < this.recordInfo.getSumNum()) {
+                logger.info("已获取" + totalRecordList.size() + "项录像数据,共" + this.recordInfo.getSumNum() + "项");
+            } else {
+                logger.info("录像数据已全部获取,共 {} 项", this.recordInfo.getSumNum());
+                this.recordInfo.setRecordList(totalRecordList);
+                for (int i = 0; i < cacheKeys.size(); i++) {
+                    redis.del(cacheKeys.get(i).toString());
+                }
+                break;
+            }
+        }
+        // 自然顺序排序, 元素进行升序排列
+        this.recordInfo.getRecordList().sort(Comparator.naturalOrder());
+        RequestMessage msg = new RequestMessage();
+        String deviceId = recordInfo.getDeviceId();
+        msg.setDeviceId(deviceId);
+        msg.setType(DeferredResultHolder.CALLBACK_CMD_RECORDINFO);
+        msg.setData(recordInfo);
+        deferredResultHolder.invokeResult(msg);
+        logger.info("处理完成,返回结果");
+        MessageRequestProcessor.threadNameList.remove(cacheKey);
+    }
+
+	public void setRedis(RedisUtil redis) {
+		this.redis = redis;
+	}
+
+	public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) {
+		this.deferredResultHolder = deferredResultHolder;
+    }
+
+    public void setLogger(Logger logger) {
+        this.logger = logger;
+    }
+
+}

+ 65 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/callback/DeferredResultHolder.java

@@ -0,0 +1,65 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.callback;
+
+import java.util.Map;
+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: dgs
+ * @date:   2020年5月8日 下午7:59:05
+ */
+@SuppressWarnings(value = {"rawtypes", "unchecked"})
+@Component
+public class DeferredResultHolder {
+
+	public static final String CALLBACK_CMD_DEVICESTATUS = "CALLBACK_DEVICESTATUS";
+
+	public static final String CALLBACK_CMD_DEVICEINFO = "CALLBACK_DEVICEINFO";
+
+	public static final String CALLBACK_CMD_DEVICECONTROL = "CALLBACK_DEVICECONTROL";
+
+	public static final String CALLBACK_CMD_DEVICECONFIG = "CALLBACK_DEVICECONFIG";
+
+	public static final String CALLBACK_CMD_CONFIGDOWNLOAD = "CALLBACK_CONFIGDOWNLOAD";
+
+	public static final String CALLBACK_CMD_CATALOG = "CALLBACK_CATALOG";
+
+	public static final String CALLBACK_CMD_RECORDINFO = "CALLBACK_RECORDINFO";
+
+	public static final String CALLBACK_CMD_PlAY = "CALLBACK_PLAY";
+
+	public static final String CALLBACK_CMD_STOP = "CALLBACK_STOP";
+
+	public static final String CALLBACK_ONVIF = "CALLBACK_ONVIF";
+
+	public static final String CALLBACK_CMD_MOBILEPOSITION = "CALLBACK_MOBILEPOSITION";
+
+	public static final String CALLBACK_CMD_PRESETQUERY = "CALLBACK_PRESETQUERY";
+
+	public static final String CALLBACK_CMD_ALARM = "CALLBACK_ALARM";
+
+	public static final String CALLBACK_CMD_BROADCAST = "CALLBACK_BROADCAST";
+
+	private Map<String, DeferredResult> map = new ConcurrentHashMap<String, DeferredResult>();
+
+	public void put(String key, DeferredResult result) {
+		map.put(key, result);
+	}
+
+	public DeferredResult get(String key) {
+		return map.get(key);
+	}
+
+	public void invokeResult(RequestMessage msg) {
+		DeferredResult result = map.get(msg.getId());
+		if (result == null) {
+			return;
+		}
+		result.setResult(new ResponseEntity<>(msg.getData(),HttpStatus.OK));
+	}
+}

+ 51 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/callback/RequestMessage.java

@@ -0,0 +1,51 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.callback;
+
+/**
+ * @Description: 请求信息定义
+ * @author: dgs
+ * @date:   2020年5月8日 下午1:09:18
+ */
+public class RequestMessage {
+
+	private String id;
+
+	private String deviceId;
+
+	private String type;
+
+	private Object data;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getDeviceId() {
+		return deviceId;
+	}
+
+	public void setDeviceId(String deviceId) {
+		this.deviceId = deviceId;
+		this.id = type + deviceId;
+	}
+
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+		this.id = type + deviceId;
+	}
+
+	public Object getData() {
+		return data;
+	}
+
+	public void setData(Object data) {
+		this.data = data;
+	}
+}

+ 302 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java

@@ -0,0 +1,302 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.cmd;
+
+import com.zd.alg.iot.vmp.service.bean.SSRCInfo;
+import com.zd.alg.iot.vmp.gb28181.bean.Device;
+import com.zd.alg.iot.vmp.gb28181.event.SipSubscribe;
+import com.zd.alg.iot.vmp.media.zlm.ZLMHttpHookSubscribe;
+import com.zd.alg.iot.vmp.media.zlm.dto.MediaServerItem;
+
+/**
+ * @Description:设备能力接口,用于定义设备的控制、查询能力
+ * @author: dgs
+ * @date:   2020年5月3日 下午9:16:34
+ */
+public interface ISIPCommander {
+
+	/**
+	 * 云台方向放控制,使用配置文件中的默认镜头移动速度
+	 *
+	 * @param device  控制设备
+	 * @param channelId  预览通道
+	 * @param leftRight  镜头左移右移 0:停止 1:左移 2:右移
+     * @param upDown     镜头上移下移 0:停止 1:上移 2:下移
+     * @param moveSpeed  镜头移动速度
+	 */
+	boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown);
+
+	/**
+	 * 云台方向放控制
+	 *
+	 * @param device  控制设备
+	 * @param channelId  预览通道
+	 * @param leftRight  镜头左移右移 0:停止 1:左移 2:右移
+     * @param upDown     镜头上移下移 0:停止 1:上移 2:下移
+     * @param moveSpeed  镜头移动速度
+	 */
+	boolean ptzdirectCmd(Device device, String channelId, int leftRight, int upDown, int moveSpeed);
+
+	/**
+	 * 云台缩放控制,使用配置文件中的默认镜头缩放速度
+	 *
+	 * @param device  控制设备
+	 * @param channelId  预览通道
+     * @param inOut      镜头放大缩小 0:停止 1:缩小 2:放大
+	 */
+	boolean ptzZoomCmd(Device device, String channelId, int inOut);
+
+	/**
+	 * 云台缩放控制
+	 *
+	 * @param device  控制设备
+	 * @param channelId  预览通道
+     * @param inOut      镜头放大缩小 0:停止 1:缩小 2:放大
+     * @param zoomSpeed  镜头缩放速度
+	 */
+	boolean ptzZoomCmd(Device device, String channelId, int inOut, int moveSpeed);
+
+	/**
+	 * 云台控制,支持方向与缩放控制
+	 *
+	 * @param device  控制设备
+	 * @param channelId  预览通道
+	 * @param leftRight  镜头左移右移 0:停止 1:左移 2:右移
+     * @param upDown     镜头上移下移 0:停止 1:上移 2:下移
+     * @param inOut      镜头放大缩小 0:停止 1:缩小 2:放大
+     * @param moveSpeed  镜头移动速度
+     * @param zoomSpeed  镜头缩放速度
+	 */
+	boolean ptzCmd(Device device, String channelId, int leftRight, int upDown, int inOut, int moveSpeed, int zoomSpeed);
+
+	/**
+	 * 前端控制,包括PTZ指令、FI指令、预置位指令、巡航指令、扫描指令和辅助开关指令
+	 *
+	 * @param device  		控制设备
+	 * @param channelId		预览通道
+	 * @param cmdCode		指令码
+     * @param parameter1	数据1
+     * @param parameter2	数据2
+     * @param combineCode2	组合码2
+	 */
+	boolean frontEndCmd(Device device, String channelId, int cmdCode, int parameter1, int parameter2, int combineCode2);
+
+	/**
+	 * 前端控制指令(用于转发上级指令)
+	 * @param device		控制设备
+	 * @param channelId		预览通道
+	 * @param cmdString		前端控制指令串
+	 */
+	boolean fronEndCmd(Device device, String channelId, String cmdString);
+
+	/**
+	 * 请求预览视频流
+	 * @param device  视频设备
+	 * @param channelId  预览通道
+	 */
+	void playStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 请求回放视频流
+	 *
+	 * @param device  视频设备
+	 * @param channelId  预览通道
+	 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
+	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
+	 */
+	void playbackStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInf, Device device, String channelId, String startTime, String endTime, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 请求历史媒体下载
+	 *
+	 * @param device  视频设备
+	 * @param channelId  预览通道
+	 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
+	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
+	 * @param downloadSpeed 下载倍速参数
+	 */
+	void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId, String startTime, String endTime, String downloadSpeed, ZLMHttpHookSubscribe.Event event, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 视频流停止
+	 */
+	void streamByeCmd(String deviceId, String channelId, SipSubscribe.Event okEvent);
+	void streamByeCmd(String deviceId, String channelId);
+
+	/**
+	 * 语音广播
+	 *
+	 * @param device  视频设备
+	 * @param channelId  预览通道
+	 */
+	boolean audioBroadcastCmd(Device device, String channelId);
+
+	/**
+	 * 语音广播
+	 *
+	 * @param device  视频设备
+	 */
+	void audioBroadcastCmd(Device device, SipSubscribe.Event okEvent);
+	boolean audioBroadcastCmd(Device device);
+
+	/**
+	 * 音视频录像控制
+	 *
+	 * @param device  		视频设备
+	 * @param channelId  	预览通道
+	 * @param recordCmdStr	录像命令:Record / StopRecord
+	 */
+	boolean recordCmd(Device device, String channelId, String recordCmdStr, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 远程启动控制命令
+	 *
+	 * @param device	视频设备
+	 */
+	boolean teleBootCmd(Device device);
+
+	/**
+	 * 报警布防/撤防命令
+	 *
+	 * @param device  	视频设备
+	 * @param setGuard	true: SetGuard, false: ResetGuard
+	 */
+	boolean guardCmd(Device device, String guardCmdStr, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 报警复位命令
+	 *
+	 * @param device		视频设备
+	 * @param alarmMethod	报警方式(可选)
+	 * @param alarmType		报警类型(可选)
+	 */
+	boolean alarmCmd(Device device, String alarmMethod, String alarmType, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 强制关键帧命令,设备收到此命令应立刻发送一个IDR帧
+	 *
+	 * @param device  视频设备
+	 * @param channelId  预览通道
+	 */
+	boolean iFrameCmd(Device device, String channelId);
+
+	/**
+	 * 看守位控制命令
+	 *
+	 * @param device		视频设备
+	 * @param enabled		看守位使能:1 = 开启,0 = 关闭
+	 * @param resetTime		自动归位时间间隔,开启看守位时使用,单位:秒(s)
+	 * @param presetIndex	调用预置位编号,开启看守位时使用,取值范围0~255
+	 */
+	boolean homePositionCmd(Device device, String channelId, String enabled, String resetTime, String presetIndex, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 设备配置命令
+	 *
+	 * @param device  视频设备
+	 */
+	boolean deviceConfigCmd(Device device);
+
+		/**
+	 * 设备配置命令:basicParam
+	 *
+	 * @param device  			视频设备
+	 * @param channelId			通道编码(可选)
+	 * @param name				设备/通道名称(可选)
+	 * @param expiration		注册过期时间(可选)
+	 * @param heartBeatInterval	心跳间隔时间(可选)
+	 * @param heartBeatCount	心跳超时次数(可选)
+	 */
+	boolean deviceBasicConfigCmd(Device device, String channelId, String name, String expiration, String heartBeatInterval, String heartBeatCount, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 查询设备状态
+	 *
+	 * @param device 视频设备
+	 */
+	boolean deviceStatusQuery(Device device, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 查询设备信息
+	 *
+	 * @param device 视频设备
+	 * @return
+	 */
+	boolean deviceInfoQuery(Device device);
+
+	/**
+	 * 查询目录列表
+	 *
+	 * @param device 视频设备
+	 */
+	boolean catalogQuery(Device device, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 查询录像信息
+	 *
+	 * @param device 视频设备
+	 * @param startTime 开始时间,格式要求:yyyy-MM-dd HH:mm:ss
+	 * @param endTime 结束时间,格式要求:yyyy-MM-dd HH:mm:ss
+	 */
+	boolean recordInfoQuery(Device device, String channelId, String startTime, String endTime);
+
+	/**
+	 * 查询报警信息
+	 *
+	 * @param device		视频设备
+	 * @param startPriority	报警起始级别(可选)
+	 * @param endPriority	报警终止级别(可选)
+	 * @param alarmMethod	报警方式条件(可选)
+	 * @param alarmType		报警类型
+	 * @param startTime		报警发生起始时间(可选)
+	 * @param endTime		报警发生终止时间(可选)
+	 * @return				true = 命令发送成功
+	 */
+	boolean alarmInfoQuery(Device device, String startPriority, String endPriority, String alarmMethod,
+                           String alarmType, String startTime, String endTime, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 查询设备配置
+	 *
+	 * @param device 		视频设备
+	 * @param channelId		通道编码(可选)
+	 * @param configType	配置类型:
+	 */
+	boolean deviceConfigQuery(Device device, String channelId, String configType, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 查询设备预置位置
+	 *
+	 * @param device 视频设备
+	 */
+	boolean presetQuery(Device device, String channelId, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 查询移动设备位置数据
+	 *
+	 * @param device 视频设备
+	 */
+	boolean mobilePostitionQuery(Device device, SipSubscribe.Event errorEvent);
+
+	/**
+	 * 订阅、取消订阅移动位置
+	 *
+	 * @param device	视频设备
+	 * @param expires	订阅超时时间(值=0时为取消订阅)
+	 * @param interval	上报时间间隔
+	 * @return			true = 命令发送成功
+	 */
+	boolean mobilePositionSubscribe(Device device, int expires, int interval);
+
+	/**
+	 * 订阅、取消订阅报警信息
+	 * @param device		视频设备
+	 * @param expires		订阅过期时间(0 = 取消订阅)
+	 * @param startPriority	报警起始级别(可选)
+	 * @param endPriority	报警终止级别(可选)
+	 * @param alarmMethods	报警方式条件(可选)
+	 * @param alarmType		报警类型
+	 * @param startTime		报警发生起始时间(可选)
+	 * @param endTime		报警发生终止时间(可选)
+	 * @return				true = 命令发送成功
+	 */
+	boolean alarmSubscribe(Device device, int expires, String startPriority, String endPriority, String alarmMethod, String alarmType, String startTime, String endTime);
+}

+ 64 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java

@@ -0,0 +1,64 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.cmd;
+
+import com.zd.alg.iot.vmp.gb28181.bean.DeviceChannel;
+import com.zd.alg.iot.vmp.gb28181.bean.ParentPlatform;
+import com.zd.alg.iot.vmp.gb28181.event.SipSubscribe;
+
+import javax.sip.header.WWWAuthenticateHeader;
+
+public interface ISIPCommanderForPlatform {
+
+    /**
+     * 向上级平台注册
+     * @param parentPlatform
+     * @return
+     */
+    boolean register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent);
+    boolean register(ParentPlatform parentPlatform, String callId, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent);
+
+    /**
+     * 向上级平台注销
+     * @param parentPlatform
+     * @return
+     */
+    boolean unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent);
+
+
+    /**
+     * 向上级平发送心跳信息
+     * @param parentPlatform
+     * @return callId(作为接受回复的判定)
+     */
+    String keepalive(ParentPlatform parentPlatform);
+
+
+    /**
+     * 向上级回复通道信息
+     * @param channel 通道信息
+     * @param parentPlatform 平台信息
+     * @param sn
+     * @param fromTag
+     * @param size
+     * @return
+     */
+    boolean catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size);
+
+    /**
+     * 向上级回复DeviceInfo查询信息
+     * @param parentPlatform 平台信息
+     * @param sn
+     * @param fromTag
+     * @return
+     */
+    boolean deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag);
+
+    /**
+     * 向上级回复DeviceStatus查询信息
+     * @param parentPlatform 平台信息
+     * @param sn
+     * @param fromTag
+     * @return
+     */
+    boolean deviceStatusResponse(ParentPlatform parentPlatform, String sn, String fromTag);
+
+}

+ 226 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java

@@ -0,0 +1,226 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.cmd;
+
+import com.zd.alg.iot.vmp.conf.SipConfig;
+import com.zd.alg.iot.vmp.gb28181.bean.ParentPlatform;
+import gov.nist.javax.sip.message.MessageFactoryImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.util.DigestUtils;
+
+import javax.sip.*;
+import javax.sip.address.Address;
+import javax.sip.address.SipURI;
+import javax.sip.header.*;
+import javax.sip.message.Request;
+import javax.validation.constraints.NotNull;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * @Description: 平台命令request创造器 TODO 冗余代码太多待优化
+ * @author: panll
+ * @date: 2020年5月6日 上午9:29:02
+ */
+@Component
+public class SIPRequestHeaderPlarformProvider {
+
+	@Autowired
+	private SipConfig sipConfig;
+
+	@Autowired
+	private SipFactory sipFactory;
+
+
+	public Request createKeetpaliveMessageRequest(ParentPlatform parentPlatform, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
+		Request request = null;
+		// sipuri
+		SipURI requestURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
+		// via
+		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
+		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(),
+				parentPlatform.getTransport(), viaTag);
+		viaHeader.setRPort();
+		viaHeaders.add(viaHeader);
+		// from
+		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(),
+				sipConfig.getIp() + ":" + sipConfig.getPort());
+		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
+		// to
+		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort() );
+		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);
+
+
+		// Forwards
+		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		// ceq
+		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE);
+
+		request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
+				toHeader, viaHeaders, maxForwards);
+
+		List<String> agentParam = new ArrayList<>();
+		agentParam.add("wvp-pro");
+		UserAgentHeader userAgentHeader = sipFactory.createHeaderFactory().createUserAgentHeader(agentParam);
+		request.addHeader(userAgentHeader);
+
+		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("Application", "MANSCDP+xml");
+		request.setContent(content, contentTypeHeader);
+		return request;
+	}
+
+
+	public Request createRegisterRequest(@NotNull ParentPlatform platform, long CSeq, String fromTag, String viaTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
+		Request request = null;
+		String sipAddress = sipConfig.getIp() + ":" + sipConfig.getPort();
+		//请求行
+		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(platform.getServerGBId(),
+				platform.getServerIP() + ":" + platform.getServerPort());
+		//via
+		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
+		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(platform.getServerIP(), platform.getServerPort(), platform.getTransport(), viaTag);
+		viaHeader.setRPort();
+		viaHeaders.add(viaHeader);
+		//from
+		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(platform.getDeviceGBId(),sipAddress);
+		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
+		//to
+		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(platform.getDeviceGBId(),sipAddress);
+		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
+
+
+
+		//Forwards
+		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+
+		//ceq
+		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(CSeq, Request.REGISTER);
+		request = sipFactory.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));
+
+		ExpiresHeader expires = sipFactory.createHeaderFactory().createExpiresHeader(Integer.parseInt(platform.getExpires()));
+		request.addHeader(expires);
+
+		List<String> agentParam = new ArrayList<>();
+		agentParam.add("wvp-pro");
+		UserAgentHeader userAgentHeader = sipFactory.createHeaderFactory().createUserAgentHeader(agentParam);
+		request.addHeader(userAgentHeader);
+
+		return request;
+	}
+
+	public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, String fromTag, String viaTag,
+										 String callId, WWWAuthenticateHeader www , CallIdHeader callIdHeader) throws ParseException, PeerUnavailableException, InvalidArgumentException {
+
+
+		Request registerRequest = createRegisterRequest(parentPlatform, 2L, fromTag, viaTag, callIdHeader);
+
+		String realm = www.getRealm();
+		String nonce = www.getNonce();
+		String scheme = www.getScheme();
+
+		// 参考 https://blog.csdn.net/y673533511/article/details/88388138
+		// qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
+		String qop = www.getQop();
+
+		callIdHeader.setCallId(callId);
+
+		SipURI requestURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
+		String cNonce = null;
+		String nc = "00000001";
+		if (qop != null) {
+			if ("auth".equals(qop)) {
+				// 客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。
+				// 这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护
+				cNonce = UUID.randomUUID().toString();
+
+			}else if ("auth-int".equals(qop)){
+				// TODO
+			}
+		}
+		String HA1 = DigestUtils.md5DigestAsHex((parentPlatform.getDeviceGBId() + ":" + realm + ":" + parentPlatform.getPassword()).getBytes());
+		String HA2=DigestUtils.md5DigestAsHex((Request.REGISTER + ":" + requestURI.toString()).getBytes());
+
+		StringBuffer reStr = new StringBuffer(200);
+		reStr.append(HA1);
+		reStr.append(":");
+		reStr.append(nonce);
+		reStr.append(":");
+		if (qop != null) {
+			reStr.append(nc);
+			reStr.append(":");
+			reStr.append(cNonce);
+			reStr.append(":");
+			reStr.append(qop);
+			reStr.append(":");
+		}
+		reStr.append(HA2);
+
+		String RESPONSE = DigestUtils.md5DigestAsHex(reStr.toString().getBytes());
+
+		AuthorizationHeader authorizationHeader = sipFactory.createHeaderFactory().createAuthorizationHeader(scheme);
+		authorizationHeader.setUsername(parentPlatform.getDeviceGBId());
+		authorizationHeader.setRealm(realm);
+		authorizationHeader.setNonce(nonce);
+		authorizationHeader.setURI(requestURI);
+		authorizationHeader.setResponse(RESPONSE);
+		authorizationHeader.setAlgorithm("MD5");
+		if (qop != null) {
+			authorizationHeader.setQop(qop);
+			authorizationHeader.setCNonce(cNonce);
+			authorizationHeader.setNonceCount(1);
+		}
+		registerRequest.addHeader(authorizationHeader);
+
+		return registerRequest;
+	}
+
+
+	public Request createMessageRequest(ParentPlatform parentPlatform, String content, String fromTag, CallIdHeader callIdHeader) throws PeerUnavailableException, ParseException, InvalidArgumentException {
+		Request request = null;
+		// sipuri
+		SipURI requestURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP()+ ":" + parentPlatform.getServerPort());
+		// via
+		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
+		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(parentPlatform.getDeviceIp(), Integer.parseInt(parentPlatform.getDevicePort()),
+				parentPlatform.getTransport(), null);
+		viaHeader.setRPort();
+		viaHeaders.add(viaHeader);
+		// from
+		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(),
+				parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort());
+		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
+		// to
+		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerGBDomain());
+		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, null);
+
+		// Forwards
+		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		// ceq
+		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE);
+		MessageFactoryImpl messageFactory = (MessageFactoryImpl) sipFactory.createMessageFactory();
+		// 设置编码, 防止中文乱码
+		messageFactory.setDefaultContentEncodingCharset("gb2312");
+		request = messageFactory.createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
+				toHeader, viaHeaders, maxForwards);
+		List<String> agentParam = new ArrayList<>();
+		agentParam.add("wvp-pro");
+		UserAgentHeader userAgentHeader = sipFactory.createHeaderFactory().createUserAgentHeader(agentParam);
+		request.addHeader(userAgentHeader);
+
+		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml");
+		request.setContent(content, contentTypeHeader);
+		return request;
+	}
+}

+ 210 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderProvider.java

@@ -0,0 +1,210 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.cmd;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.PeerUnavailableException;
+import javax.sip.SipFactory;
+import javax.sip.address.Address;
+import javax.sip.address.SipURI;
+import javax.sip.header.*;
+import javax.sip.message.Request;
+
+import com.zd.alg.iot.vmp.conf.SipConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.zd.alg.iot.vmp.gb28181.bean.Device;
+
+/**
+ * @Description:摄像头命令request创造器 TODO 冗余代码太多待优化
+ * @author: dgs
+ * @date: 2020年5月6日 上午9:29:02
+ */
+@Component
+public class SIPRequestHeaderProvider {
+
+	@Autowired
+	private SipConfig sipConfig;
+
+	@Autowired
+	private SipFactory sipFactory;
+
+	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());
+		// via
+		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
+		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
+		viaHeader.setRPort();
+		viaHeaders.add(viaHeader);
+		// from
+		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),
+				sipConfig.getIp() + ":" + sipConfig.getPort());
+		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
+		// to
+		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getDomain());
+		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);
+
+		// Forwards
+		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		// ceq
+		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.MESSAGE);
+
+		request = sipFactory.createMessageFactory().createRequest(requestURI, Request.MESSAGE, callIdHeader, cSeqHeader, fromHeader,
+				toHeader, viaHeaders, maxForwards);
+		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml");
+		request.setContent(content, contentTypeHeader);
+		return request;
+	}
+
+	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());
+		//via
+		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
+		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), 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
+		//to
+		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getDomain());
+		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
+
+		//Forwards
+		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+
+		//ceq
+		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE);
+		request = sipFactory.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));
+		// Subject
+		SubjectHeader subjectHeader = sipFactory.createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0));
+		request.addHeader(subjectHeader);
+		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
+		request.setContent(content, contentTypeHeader);
+		return request;
+	}
+
+	public Request createPlaybackInviteRequest(Device device, String channelId, String content, String viaTag, String fromTag, String toTag, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
+		Request request = null;
+		//请求行
+		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
+		// via
+		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
+		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(device.getIp(), device.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
+		//to
+		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getDomain());
+		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,null);
+
+		//Forwards
+		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+
+		//ceq
+		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.INVITE);
+		request = sipFactory.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));
+
+		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
+		request.setContent(content, contentTypeHeader);
+		return request;
+	}
+
+	public Request createByteRequest(Device device, String channelId, String viaTag, String fromTag, String toTag, String callId) throws ParseException, InvalidArgumentException, PeerUnavailableException {
+		Request request = null;
+		//请求行
+		SipURI requestLine = sipFactory.createAddressFactory().createSipURI(channelId, device.getHostAddress());
+		// via
+		ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
+		ViaHeader viaHeader = sipFactory.createHeaderFactory().createViaHeader(sipConfig.getIp(), sipConfig.getPort(), device.getTransport(), viaTag);
+		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
+		//to
+		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(channelId,sipConfig.getDomain());
+		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress,toTag);
+
+		//Forwards
+		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+
+		//ceq
+		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.BYE);
+		CallIdHeader callIdHeader = sipFactory.createHeaderFactory().createCallIdHeader(callId);
+		request = sipFactory.createMessageFactory().createRequest(requestLine, Request.BYE, callIdHeader, cSeqHeader,fromHeader, toHeader, viaHeaders, maxForwards);
+
+		Address concatAddress = sipFactory.createAddressFactory().createAddress(sipFactory.createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getIp()+":"+sipConfig.getPort()));
+
+		return request;
+	}
+
+	public Request createSubscribeRequest(Device device, String content, String viaTag, String fromTag, String toTag, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
+		Request request = null;
+		// sipuri
+		SipURI requestURI = sipFactory.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.setRPort();
+		viaHeaders.add(viaHeader);
+		// from
+		SipURI fromSipURI = sipFactory.createAddressFactory().createSipURI(sipConfig.getId(),
+				sipConfig.getIp() + ":" + sipConfig.getPort());
+		Address fromAddress = sipFactory.createAddressFactory().createAddress(fromSipURI);
+		FromHeader fromHeader = sipFactory.createHeaderFactory().createFromHeader(fromAddress, fromTag);
+		// to
+		SipURI toSipURI = sipFactory.createAddressFactory().createSipURI(device.getDeviceId(), sipConfig.getDomain());
+		Address toAddress = sipFactory.createAddressFactory().createAddress(toSipURI);
+		ToHeader toHeader = sipFactory.createHeaderFactory().createToHeader(toAddress, toTag);
+
+		// Forwards
+		MaxForwardsHeader maxForwards = sipFactory.createHeaderFactory().createMaxForwardsHeader(70);
+		// ceq
+		CSeqHeader cSeqHeader = sipFactory.createHeaderFactory().createCSeqHeader(1L, Request.SUBSCRIBE);
+
+		request = sipFactory.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));
+
+		// Expires
+		ExpiresHeader expireHeader = sipFactory.createHeaderFactory().createExpiresHeader(expires);
+		request.addHeader(expireHeader);
+
+		// Event
+		EventHeader eventHeader = sipFactory.createHeaderFactory().createEventHeader(event);
+		request.addHeader(eventHeader);
+
+		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "MANSCDP+xml");
+		request.setContent(content, contentTypeHeader);
+		return request;
+	}
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1517 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java


+ 322 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java

@@ -0,0 +1,322 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.cmd.impl;
+
+import com.zd.alg.iot.vmp.storager.IRedisCatchStorage;
+import com.zd.alg.iot.vmp.gb28181.bean.DeviceChannel;
+import com.zd.alg.iot.vmp.gb28181.bean.ParentPlatform;
+import com.zd.alg.iot.vmp.gb28181.bean.ParentPlatformCatch;
+import com.zd.alg.iot.vmp.gb28181.event.SipSubscribe;
+import com.zd.alg.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
+import com.zd.alg.iot.vmp.gb28181.transmit.cmd.SIPRequestHeaderPlarformProvider;
+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 javax.sip.*;
+import javax.sip.header.CallIdHeader;
+import javax.sip.header.WWWAuthenticateHeader;
+import javax.sip.message.Request;
+import java.text.ParseException;
+import java.util.UUID;
+
+@Component
+@DependsOn("sipLayer")
+public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
+
+    private final Logger logger = LoggerFactory.getLogger(SIPCommanderFroPlatform.class);
+
+    // @Autowired
+    // private SipConfig sipConfig;
+
+    // @Autowired
+    // private SIPRequestHeaderProvider headerProvider;
+
+    @Autowired
+    private SIPRequestHeaderPlarformProvider headerProviderPlarformProvider;
+
+    // @Autowired
+    // private VideoStreamSessionManager streamSession;
+
+    // @Autowired
+    // private IVideoManagerStorager storager;
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private SipSubscribe sipSubscribe;
+
+    @Lazy
+    @Autowired
+    @Qualifier(value="tcpSipProvider")
+    private SipProvider tcpSipProvider;
+
+    @Lazy
+    @Autowired
+    @Qualifier(value="udpSipProvider")
+    private SipProvider udpSipProvider;
+
+    @Override
+    public boolean register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) {
+        return register(parentPlatform, null, null, errorEvent, okEvent);
+    }
+
+    @Override
+    public boolean unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) {
+        parentPlatform.setExpires("0");
+        ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
+        if (parentPlatformCatch != null) {
+            parentPlatformCatch.setParentPlatform(parentPlatform);
+            redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
+        }
+
+        return register(parentPlatform, null, null, errorEvent, okEvent);
+    }
+
+    @Override
+    public boolean register(ParentPlatform parentPlatform, @Nullable String callId, @Nullable WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) {
+        try {
+            Request request = null;
+            String tm = Long.toString(System.currentTimeMillis());
+            if (www == null ) {
+                //		//callid
+                CallIdHeader callIdHeader = null;
+                if(parentPlatform.getTransport().equals("TCP")) {
+                    callIdHeader = tcpSipProvider.getNewCallId();
+                }
+                if(parentPlatform.getTransport().equals("UDP")) {
+                    callIdHeader = udpSipProvider.getNewCallId();
+                }
+
+                request = headerProviderPlarformProvider.createRegisterRequest(parentPlatform, 1L, "FromRegister" + tm, null, callIdHeader);
+                // 将 callid 写入缓存, 等注册成功可以更新状态
+                redisCatchStorage.updatePlatformRegisterInfo(callIdHeader.getCallId(), parentPlatform.getServerGBId());
+
+                sipSubscribe.addErrorSubscribe(callIdHeader.getCallId(), (event)->{
+                    if (event != null) {
+                        logger.info("向上级平台 [ {} ] 注册发上错误: {} ",
+                                parentPlatform.getServerGBId(),
+                                event.getResponse().getReasonPhrase());
+                    }
+                    if (errorEvent != null ) {
+                        errorEvent.response(event);
+                    }
+                });
+
+            }else {
+                CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                        : udpSipProvider.getNewCallId();
+                request = headerProviderPlarformProvider.createRegisterRequest(parentPlatform, "FromRegister" + tm, null, callId, www, callIdHeader);
+            }
+
+            transmitRequest(parentPlatform, request, null, okEvent);
+            return true;
+        } catch (ParseException e) {
+            e.printStackTrace();
+        } catch (InvalidArgumentException e) {
+            e.printStackTrace();
+        } catch (PeerUnavailableException e) {
+            e.printStackTrace();
+        } catch (SipException e) {
+            e.printStackTrace();
+        }
+        return false;
+    }
+
+    @Override
+    public String keepalive(ParentPlatform parentPlatform) {
+        String callId = null;
+        try {
+
+            StringBuffer keepaliveXml = new StringBuffer(200);
+            keepaliveXml.append("<?xml version=\"1.0\"?>\r\n");
+            keepaliveXml.append("<Notify>\r\n");
+            keepaliveXml.append("<CmdType>Keepalive</CmdType>\r\n");
+            keepaliveXml.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n");
+            keepaliveXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
+            keepaliveXml.append("<Status>OK</Status>\r\n");
+            keepaliveXml.append("</Notify>\r\n");
+
+            CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                    : udpSipProvider.getNewCallId();
+
+            Request request = headerProviderPlarformProvider.createKeetpaliveMessageRequest(
+                    parentPlatform,
+                    keepaliveXml.toString(),
+                    "z9hG4bK-" + UUID.randomUUID().toString().replace("-", ""),
+                    UUID.randomUUID().toString().replace("-", ""),
+                    null,
+                    callIdHeader);
+            transmitRequest(parentPlatform, request);
+            callId = callIdHeader.getCallId();
+        } catch (ParseException | InvalidArgumentException | SipException e) {
+            e.printStackTrace();
+        }
+        return callId;
+    }
+
+    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 {
+        if("TCP".equals(parentPlatform.getTransport())) {
+            tcpSipProvider.sendRequest(request);
+
+        } else if("UDP".equals(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 通道信息
+     * @param parentPlatform 平台信息
+     * @return
+     */
+    @Override
+    public boolean catalogQuery(DeviceChannel channel, ParentPlatform parentPlatform, String sn, String fromTag, int size) {
+
+        if ( parentPlatform ==null) {
+            return false;
+        }
+        try {
+            StringBuffer catalogXml = new StringBuffer(600);
+            catalogXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
+            catalogXml.append("<Response>\r\n");
+            catalogXml.append("<CmdType>Catalog</CmdType>\r\n");
+            catalogXml.append("<SN>" +sn + "</SN>\r\n");
+            catalogXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
+            catalogXml.append("<SumNum>" + size + "</SumNum>\r\n");
+            catalogXml.append("<DeviceList Num=\"1\">\r\n");
+            catalogXml.append("<Item>\r\n");
+            if (channel != null) {
+                catalogXml.append("<DeviceID>" + channel.getChannelId() + "</DeviceID>\r\n");
+                catalogXml.append("<Name>" + channel.getName() + "</Name>\r\n");
+                catalogXml.append("<Manufacturer>" + channel.getManufacture() + "</Manufacturer>\r\n");
+                catalogXml.append("<Model>" + channel.getModel() + "</Model>\r\n");
+                catalogXml.append("<Owner>" + channel.getOwner() + "</Owner>\r\n");
+                catalogXml.append("<CivilCode>" + channel.getCivilCode() + "</CivilCode>\r\n");
+                catalogXml.append("<Address>" + channel.getAddress() + "</Address>\r\n");
+                catalogXml.append("<Parental>" + channel.getParental() + "</Parental>\r\n");// TODO 当前不能添加分组, 所以暂时没有父节点
+                catalogXml.append("<ParentID>" + channel.getParentId() + "</ParentID>\r\n"); // TODO 当前不能添加分组, 所以暂时没有父节点
+                catalogXml.append("<Secrecy>" + channel.getSecrecy() + "</Secrecy>\r\n");
+                catalogXml.append("<RegisterWay>" + channel.getRegisterWay() + "</RegisterWay>\r\n");
+                catalogXml.append("<Status>" + (channel.getStatus() == 0?"OFF":"ON") + "</Status>\r\n");
+                catalogXml.append("<Info></Info>\r\n");
+            }
+
+
+            catalogXml.append("</Item>\r\n");
+            catalogXml.append("</DeviceList>\r\n");
+            catalogXml.append("</Response>\r\n");
+
+            // callid
+            CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                    : udpSipProvider.getNewCallId();
+
+            Request request = headerProviderPlarformProvider.createMessageRequest(parentPlatform, catalogXml.toString(), fromTag, callIdHeader);
+            transmitRequest(parentPlatform, request);
+
+        } catch (SipException | ParseException | InvalidArgumentException e) {
+            e.printStackTrace();
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 向上级回复DeviceInfo查询信息
+     * @param parentPlatform 平台信息
+     * @param sn
+     * @param fromTag
+     * @return
+     */
+    @Override
+    public boolean deviceInfoResponse(ParentPlatform parentPlatform, String sn, String fromTag) {
+        if (parentPlatform == null) {
+            return false;
+        }
+        try {
+            StringBuffer deviceInfoXml = new StringBuffer(600);
+            deviceInfoXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
+            deviceInfoXml.append("<Response>\r\n");
+            deviceInfoXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
+            deviceInfoXml.append("<SN>" +sn + "</SN>\r\n");
+            deviceInfoXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
+            deviceInfoXml.append("<DeviceName>" + parentPlatform.getName() + "</DeviceName>\r\n");
+            deviceInfoXml.append("<Manufacturer>wvp</Manufacturer>\r\n");
+            deviceInfoXml.append("<Model>wvp-28181-2.0</Model>\r\n");
+            deviceInfoXml.append("<Firmware>2.0.202107</Firmware>\r\n");
+            deviceInfoXml.append("<Result>OK</Result>\r\n");
+            deviceInfoXml.append("</Response>\r\n");
+
+            CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                    : udpSipProvider.getNewCallId();
+
+            Request request = headerProviderPlarformProvider.createMessageRequest(parentPlatform, deviceInfoXml.toString(), fromTag, callIdHeader);
+            transmitRequest(parentPlatform, request);
+
+        } catch (SipException | ParseException | InvalidArgumentException e) {
+            e.printStackTrace();
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * 向上级回复DeviceStatus查询信息
+     * @param parentPlatform 平台信息
+     * @param sn
+     * @param fromTag
+     * @return
+     */
+    @Override
+    public boolean deviceStatusResponse(ParentPlatform parentPlatform, String sn, String fromTag) {
+        if (parentPlatform == null) {
+            return false;
+        }
+        try {
+            StringBuffer deviceStatusXml = new StringBuffer(600);
+            deviceStatusXml.append("<?xml version=\"1.0\" encoding=\"GB2312\"?>\r\n");
+            deviceStatusXml.append("<Response>\r\n");
+            deviceStatusXml.append("<CmdType>DeviceStatus</CmdType>\r\n");
+            deviceStatusXml.append("<SN>" +sn + "</SN>\r\n");
+            deviceStatusXml.append("<DeviceID>" + parentPlatform.getDeviceGBId() + "</DeviceID>\r\n");
+            deviceStatusXml.append("<Result>OK</Result>\r\n");
+            deviceStatusXml.append("<Online>ONLINE</Online>\r\n");
+            deviceStatusXml.append("<Status>OK</Status>\r\n");
+            deviceStatusXml.append("</Response>\r\n");
+
+            CallIdHeader callIdHeader = parentPlatform.getTransport().equals("TCP") ? tcpSipProvider.getNewCallId()
+                    : udpSipProvider.getNewCallId();
+
+            Request request = headerProviderPlarformProvider.createMessageRequest(parentPlatform, deviceStatusXml.toString(), fromTag, callIdHeader);
+            transmitRequest(parentPlatform, request);
+
+        } catch (SipException | ParseException | InvalidArgumentException e) {
+            e.printStackTrace();
+            return false;
+        }
+        return true;
+    }
+}

+ 12 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/ISIPRequestProcessor.java

@@ -0,0 +1,12 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.request;
+
+/**
+ * @Description:处理接收IPCamera发来的SIP协议请求消息
+ * @author: dgs
+ * @date:   2020年5月3日 下午4:42:22
+ */
+public interface ISIPRequestProcessor {
+
+	public void process();
+
+}

+ 131 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/SIPRequestAbstractProcessor.java

@@ -0,0 +1,131 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.request;
+
+import javax.sip.PeerUnavailableException;
+import javax.sip.RequestEvent;
+import javax.sip.ServerTransaction;
+import javax.sip.SipFactory;
+import javax.sip.SipProvider;
+import javax.sip.TransactionAlreadyExistsException;
+import javax.sip.TransactionUnavailableException;
+import javax.sip.address.AddressFactory;
+import javax.sip.header.HeaderFactory;
+import javax.sip.header.ViaHeader;
+import javax.sip.message.MessageFactory;
+import javax.sip.message.Request;
+
+import gov.nist.javax.sip.SipStackImpl;
+import gov.nist.javax.sip.message.SIPRequest;
+import gov.nist.javax.sip.stack.SIPServerTransaction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @Description:处理接收IPCamera发来的SIP协议请求消息
+ * @author: songww
+ * @date:   2020年5月3日 下午4:42:22
+ */
+public abstract class SIPRequestAbstractProcessor implements ISIPRequestProcessor {
+
+	private final static Logger logger = LoggerFactory.getLogger(SIPRequestAbstractProcessor.class);
+
+	protected RequestEvent evt;
+
+	private SipProvider tcpSipProvider;
+
+	private SipProvider udpSipProvider;
+
+	@Override
+	public void process() {
+		this.process(evt);
+	}
+
+	public abstract void process(RequestEvent evt);
+
+	public ServerTransaction getServerTransaction(RequestEvent evt) {
+		Request request = evt.getRequest();
+		ServerTransaction serverTransaction = evt.getServerTransaction();
+		// 判断TCP还是UDP
+		boolean isTcp = false;
+		ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
+		String transport = reqViaHeader.getTransport();
+		if (transport.equals("TCP")) {
+			isTcp = true;
+		}
+
+		if (serverTransaction == null) {
+			try {
+				if (isTcp) {
+					SipStackImpl stack = (SipStackImpl)tcpSipProvider.getSipStack();
+					serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);
+					if (serverTransaction == null) {
+						serverTransaction = tcpSipProvider.getNewServerTransaction(request);
+					}
+				} else {
+					SipStackImpl stack = (SipStackImpl)udpSipProvider.getSipStack();
+					serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest)request, true);
+					if (serverTransaction == null) {
+						serverTransaction = 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();
+		} catch (PeerUnavailableException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+	public HeaderFactory getHeaderFactory() {
+		try {
+			return SipFactory.getInstance().createHeaderFactory();
+		} catch (PeerUnavailableException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+	public MessageFactory getMessageFactory() {
+		try {
+			return SipFactory.getInstance().createMessageFactory();
+		} catch (PeerUnavailableException e) {
+			e.printStackTrace();
+		}
+		return null;
+	}
+
+	public RequestEvent getRequestEvent() {
+		return evt;
+	}
+
+	public void setRequestEvent(RequestEvent evt) {
+		this.evt = evt;
+	}
+
+	public SipProvider getTcpSipProvider() {
+		return tcpSipProvider;
+	}
+
+	public void setTcpSipProvider(SipProvider tcpSipProvider) {
+		this.tcpSipProvider = tcpSipProvider;
+	}
+
+	public SipProvider getUdpSipProvider() {
+		return udpSipProvider;
+	}
+
+	public void setUdpSipProvider(SipProvider udpSipProvider) {
+		this.udpSipProvider = udpSipProvider;
+	}
+
+
+}

+ 142 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/AckRequestProcessor.java

@@ -0,0 +1,142 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.request.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.sip.*;
+import javax.sip.address.SipURI;
+import javax.sip.header.FromHeader;
+import javax.sip.header.HeaderAddress;
+import javax.sip.header.ToHeader;
+
+import com.zd.alg.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
+import com.zd.alg.iot.vmp.service.IMediaServerService;
+import com.zd.alg.iot.vmp.storager.IRedisCatchStorage;
+import com.zd.alg.iot.vmp.common.StreamInfo;
+import com.zd.alg.iot.vmp.gb28181.bean.SendRtpItem;
+import com.zd.alg.iot.vmp.media.zlm.ZLMRTPServerFactory;
+import com.zd.alg.iot.vmp.media.zlm.dto.MediaServerItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @Description:ACK请求处理器
+ * @author: dgs
+ * @date:   2020年5月3日 下午5:31:45
+ */
+public class AckRequestProcessor extends SIPRequestAbstractProcessor {
+
+	private Logger logger = LoggerFactory.getLogger(AckRequestProcessor.class);
+
+    private IRedisCatchStorage redisCatchStorage;
+
+	private ZLMRTPServerFactory zlmrtpServerFactory;
+
+	private IMediaServerService mediaServerService;
+
+	/**
+	 * 处理  ACK请求
+	 *
+	 * @param evt
+	 */
+	@Override
+	public void process(RequestEvent evt) {
+		//Request request = evt.getRequest();
+		Dialog dialog = evt.getDialog();
+		if (dialog == null) return;
+		//DialogState state = dialog.getState();
+		if (/*request.getMecodewwthod().equals(Request.INVITE) &&*/ dialog.getState()== DialogState.CONFIRMED) {
+			String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
+			String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
+			SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(platformGbId, channelId);
+			String is_Udp = sendRtpItem.isTcp() ? "0" : "1";
+			String deviceId = sendRtpItem.getDeviceId();
+			StreamInfo streamInfo = null;
+			if (deviceId == null) {
+				streamInfo = new StreamInfo();
+				streamInfo.setApp(sendRtpItem.getApp());
+				streamInfo.setStreamId(sendRtpItem.getStreamId());
+			}else {
+				streamInfo = redisCatchStorage.queryPlayByDevice(deviceId, channelId);
+				sendRtpItem.setStreamId(streamInfo.getStreamId());
+				streamInfo.setApp("rtp");
+			}
+
+			redisCatchStorage.updateSendRTPSever(sendRtpItem);
+			logger.info(platformGbId);
+			logger.info(channelId);
+			Map<String, Object> param = new HashMap<>();
+			param.put("vhost","__defaultVhost__");
+			param.put("app",streamInfo.getApp());
+			param.put("stream",streamInfo.getStreamId());
+			param.put("ssrc", sendRtpItem.getSsrc());
+			param.put("dst_url",sendRtpItem.getIp());
+			param.put("dst_port", sendRtpItem.getPort());
+			param.put("is_udp", is_Udp);
+			//param.put ("src_port", sendRtpItem.getLocalPort());
+			// 设备推流查询,成功后才能转推
+			boolean rtpPushed = false;
+			long startTime = System.currentTimeMillis();
+			while (!rtpPushed) {
+				try {
+					if (System.currentTimeMillis() - startTime < 30 * 1000) {
+						MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+						if (zlmrtpServerFactory.isStreamReady(mediaInfo, streamInfo.getApp(), streamInfo.getStreamId())) {
+							rtpPushed = true;
+							logger.info("已获取设备推流[{}/{}],开始向上级推流[{}:{}]",
+									streamInfo.getApp() ,streamInfo.getStreamId(), sendRtpItem.getIp(), sendRtpItem.getPort());
+							zlmrtpServerFactory.startSendRtpStream(mediaInfo, param);
+						} else {
+							logger.info("等待设备推流[{}/{}].......",
+									streamInfo.getApp() ,streamInfo.getStreamId());
+							Thread.sleep(1000);
+							continue;
+						}
+					} else {
+						rtpPushed = true;
+						logger.info("设备推流[{}/{}]超时,终止向上级推流",
+								streamInfo.getApp() ,streamInfo.getStreamId());
+					}
+				} catch (InterruptedException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+		// try {
+		// 	Request ackRequest = null;
+		// 	CSeq csReq = (CSeq) request.getHeader(CSeq.NAME);
+		// 	ackRequest = dialog.createAck(csReq.getSeqNumber());
+		// 	dialog.sendAck(ackRequest);
+		// 	logger.info("send ack to callee:" + ackRequest.toString());
+		// } catch (SipException e) {
+		// 	e.printStackTrace();
+		// } catch (InvalidArgumentException e) {
+		// 	e.printStackTrace();
+		// }
+
+	}
+
+	public IRedisCatchStorage getRedisCatchStorage() {
+		return redisCatchStorage;
+	}
+
+	public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) {
+		this.redisCatchStorage = redisCatchStorage;
+	}
+
+	public ZLMRTPServerFactory getZlmrtpServerFactory() {
+		return zlmrtpServerFactory;
+	}
+
+	public void setZlmrtpServerFactory(ZLMRTPServerFactory zlmrtpServerFactory) {
+		this.zlmrtpServerFactory = zlmrtpServerFactory;
+	}
+
+	public IMediaServerService getMediaServerService() {
+		return mediaServerService;
+	}
+
+	public void setMediaServerService(IMediaServerService mediaServerService) {
+		this.mediaServerService = mediaServerService;
+	}
+}

+ 150 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/ByeRequestProcessor.java

@@ -0,0 +1,150 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.request.impl;
+
+import javax.sip.*;
+import javax.sip.address.SipURI;
+import javax.sip.header.FromHeader;
+import javax.sip.header.HeaderAddress;
+import javax.sip.header.ToHeader;
+import javax.sip.message.Response;
+
+import com.zd.alg.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
+import com.zd.alg.iot.vmp.service.IMediaServerService;
+import com.zd.alg.iot.vmp.storager.IRedisCatchStorage;
+import com.zd.alg.iot.vmp.storager.IVideoManagerStorager;
+import com.zd.alg.iot.vmp.common.StreamInfo;
+import com.zd.alg.iot.vmp.gb28181.bean.Device;
+import com.zd.alg.iot.vmp.gb28181.bean.SendRtpItem;
+import com.zd.alg.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
+import com.zd.alg.iot.vmp.media.zlm.ZLMRTPServerFactory;
+import com.zd.alg.iot.vmp.media.zlm.dto.MediaServerItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Description: BYE请求处理器
+ * @author: lawrencehj
+ * @date:   2021年3月9日
+ */
+public class ByeRequestProcessor extends SIPRequestAbstractProcessor {
+
+	private Logger logger = LoggerFactory.getLogger(ByeRequestProcessor.class);
+
+	private ISIPCommander cmder;
+
+	private IRedisCatchStorage redisCatchStorage;
+
+	private IVideoManagerStorager storager;
+
+	private ZLMRTPServerFactory zlmrtpServerFactory;
+
+	private IMediaServerService mediaServerService;
+
+	/**
+	 * 处理BYE请求
+	 * @param evt
+	 */
+	@Override
+	public void process(RequestEvent evt) {
+		try {
+			responseAck(evt);
+			Dialog dialog = evt.getDialog();
+			if (dialog == null) return;
+			if (dialog.getState().equals(DialogState.TERMINATED)) {
+				String platformGbId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI()).getUser();
+				String channelId = ((SipURI) ((HeaderAddress) evt.getRequest().getHeader(ToHeader.NAME)).getAddress().getURI()).getUser();
+				SendRtpItem sendRtpItem =  redisCatchStorage.querySendRTPServer(platformGbId, channelId);
+				logger.info("收到bye, [{}/{}]", platformGbId, channelId);
+				if (sendRtpItem != null){
+					String streamId = sendRtpItem.getStreamId();
+					Map<String, Object> param = new HashMap<>();
+					param.put("vhost","__defaultVhost__");
+					param.put("app",sendRtpItem.getApp());
+					param.put("stream",streamId);
+					param.put("ssrc",sendRtpItem.getSsrc());
+					logger.info("停止向上级推流:" + streamId);
+					MediaServerItem mediaInfo = mediaServerService.getOne(sendRtpItem.getMediaServerId());
+					zlmrtpServerFactory.stopSendRtpStream(mediaInfo, param);
+					redisCatchStorage.deleteSendRTPServer(platformGbId, channelId);
+					if (zlmrtpServerFactory.totalReaderCount(mediaInfo, sendRtpItem.getApp(), streamId) == 0) {
+						logger.info(streamId + "无其它观看者,通知设备停止推流");
+						cmder.streamByeCmd(sendRtpItem.getDeviceId(), channelId);
+					}
+				}
+				// 可能是设备主动停止
+				Device device = storager.queryVideoDeviceByChannelId(platformGbId);
+				if (device != null) {
+					StreamInfo streamInfo = redisCatchStorage.queryPlayByDevice(device.getDeviceId(), channelId);
+					if (streamInfo != null) {
+						redisCatchStorage.stopPlay(streamInfo);
+					}
+					storager.stopPlay(device.getDeviceId(), channelId);
+					mediaServerService.closeRTPServer(device, channelId);
+				}
+			}
+		} catch (SipException e) {
+			e.printStackTrace();
+		} catch (InvalidArgumentException e) {
+			e.printStackTrace();
+		} catch (ParseException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/***
+	 * 回复200 OK
+	 * @param evt
+	 * @throws SipException
+	 * @throws InvalidArgumentException
+	 * @throws ParseException
+	 */
+	private void responseAck(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException {
+		Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
+		ServerTransaction serverTransaction = getServerTransaction(evt);
+		serverTransaction.sendResponse(response);
+		if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
+	}
+
+	public IRedisCatchStorage getRedisCatchStorage() {
+		return redisCatchStorage;
+	}
+
+	public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) {
+		this.redisCatchStorage = redisCatchStorage;
+	}
+
+	public ZLMRTPServerFactory getZlmrtpServerFactory() {
+		return zlmrtpServerFactory;
+	}
+
+	public void setZlmrtpServerFactory(ZLMRTPServerFactory zlmrtpServerFactory) {
+		this.zlmrtpServerFactory = zlmrtpServerFactory;
+	}
+
+	public ISIPCommander getSIPCommander() {
+		return cmder;
+	}
+
+	public void setSIPCommander(ISIPCommander cmder) {
+		this.cmder = cmder;
+	}
+
+	public IMediaServerService getMediaServerService() {
+		return mediaServerService;
+	}
+
+	public void setMediaServerService(IMediaServerService mediaServerService) {
+		this.mediaServerService = mediaServerService;
+	}
+
+	public IVideoManagerStorager getStorager() {
+		return storager;
+	}
+
+	public void setStorager(IVideoManagerStorager storager) {
+		this.storager = storager;
+	}
+}

+ 28 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/CancelRequestProcessor.java

@@ -0,0 +1,28 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.request.impl;
+
+import javax.sip.RequestEvent;
+
+import com.zd.alg.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
+
+/**
+ * @Description:CANCEL请求处理器
+ * @author: dgs
+ * @date:   2020年5月3日 下午5:32:23
+ */
+public class CancelRequestProcessor extends SIPRequestAbstractProcessor {
+
+	/**
+	 * 处理CANCEL请求
+	 *
+	 * @param evt
+	 * @param layer
+	 * @param transaction
+	 * @param config
+	 */
+	@Override
+	public void process(RequestEvent evt) {
+		// TODO 优先级99 Cancel Request消息实现,此消息一般为级联消息,上级给下级发送请求取消指令
+
+	}
+
+}

+ 478 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java

@@ -0,0 +1,478 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.request.impl;
+
+import com.zd.alg.iot.vmp.gb28181.bean.*;
+import com.zd.alg.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
+import com.zd.alg.iot.vmp.service.IMediaServerService;
+import com.zd.alg.iot.vmp.service.IPlayService;
+import com.zd.alg.iot.vmp.storager.IRedisCatchStorage;
+import com.zd.alg.iot.vmp.storager.IVideoManagerStorager;
+import com.zd.alg.iot.vmp.vmanager.gb28181.play.bean.PlayResult;
+import com.zd.alg.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import com.zd.alg.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
+import com.zd.alg.iot.vmp.media.zlm.ZLMRTPServerFactory;
+import com.zd.alg.iot.vmp.media.zlm.dto.MediaServerItem;
+import gov.nist.javax.sip.address.AddressImpl;
+import gov.nist.javax.sip.address.SipUri;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.sdp.*;
+import javax.sip.*;
+import javax.sip.address.Address;
+import javax.sip.address.SipURI;
+import javax.sip.header.ContentTypeHeader;
+import javax.sip.header.FromHeader;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+import java.text.ParseException;
+import java.util.Vector;
+
+/**
+ * @Description:处理INVITE请求
+ * @author: panll
+ * @date:   2021年1月14日
+ */
+@SuppressWarnings("rawtypes")
+public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
+
+	private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class);
+
+	private SIPCommanderFroPlatform cmderFroPlatform;
+
+	private IVideoManagerStorager storager;
+
+	private IRedisCatchStorage redisCatchStorage;
+
+	private SIPCommander cmder;
+
+	private IPlayService playService;
+
+	private ZLMRTPServerFactory zlmrtpServerFactory;
+
+	private IMediaServerService mediaServerService;
+
+	public ZLMRTPServerFactory getZlmrtpServerFactory() {
+		return zlmrtpServerFactory;
+	}
+
+	public void setZlmrtpServerFactory(ZLMRTPServerFactory zlmrtpServerFactory) {
+		this.zlmrtpServerFactory = zlmrtpServerFactory;
+	}
+
+	/**
+	 * 处理invite请求
+	 *
+	 * @param evt
+	 *            请求消息
+	 */
+	@Override
+	public void process(RequestEvent evt) {
+		//  Invite Request消息实现,此消息一般为级联消息,上级给下级发送请求视频指令
+		try {
+			Request request = evt.getRequest();
+			SipURI sipURI = (SipURI) request.getRequestURI();
+			String channelId = sipURI.getUser();
+			String requesterId = null;
+
+			FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME);
+			AddressImpl address = (AddressImpl) fromHeader.getAddress();
+			SipUri uri = (SipUri) address.getURI();
+			requesterId = uri.getUser();
+
+			if (requesterId == null || channelId == null) {
+				logger.info("无法从FromHeader的Address中获取到平台id,返回400");
+				responseAck(evt, Response.BAD_REQUEST); // 参数不全, 发400,请求错误
+				return;
+			}
+
+			// 查询请求方是否上级平台
+			ParentPlatform platform = storager.queryParentPlatByServerGBId(requesterId);
+			if (platform != null) {
+				// 查询平台下是否有该通道
+				DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);
+				GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
+				MediaServerItem mediaServerItem = null;
+				// 不是通道可能是直播流
+				if (channel != null && gbStream == null ) {
+					if (channel.getStatus() == 0) {
+						logger.info("通道离线,返回400");
+						responseAck(evt, Response.BAD_REQUEST, "channel [" + channel.getChannelId() + "] offline");
+						return;
+					}
+					responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中
+				}else if(channel == null && gbStream != null){
+					String mediaServerId = gbStream.getMediaServerId();
+					mediaServerItem = mediaServerService.getOne(mediaServerId);
+					if (mediaServerItem == null) {
+						logger.info("[ app={}, stream={} ]zlm找不到,返回410",gbStream.getApp(), gbStream.getStream());
+						responseAck(evt, Response.GONE, "media server not found");
+						return;
+					}
+					Boolean streamReady = zlmrtpServerFactory.isStreamReady(mediaServerItem, gbStream.getApp(), gbStream.getStream());
+					if (!streamReady ) {
+						logger.info("[ app={}, stream={} ]通道离线,返回400",gbStream.getApp(), gbStream.getStream());
+						responseAck(evt, Response.BAD_REQUEST, "channel [" + gbStream.getGbId() + "] offline");
+						return;
+					}
+					responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 通道存在,发181,呼叫转接中
+				}else {
+					logger.info("通道不存在,返回404");
+					responseAck(evt, Response.NOT_FOUND); // 通道不存在,发404,资源不存在
+					return;
+				}
+				// 解析sdp消息, 使用jainsip 自带的sdp解析方式
+				String contentString = new String(request.getRawContent());
+
+				// jainSip不支持y=字段, 移除移除以解析。
+				int ssrcIndex = contentString.indexOf("y=");
+				//ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段
+				String ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
+				String substring = contentString.substring(0, contentString.indexOf("y="));
+				SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
+
+				//  获取支持的格式
+				Vector mediaDescriptions = sdp.getMediaDescriptions(true);
+				// 查看是否支持PS 负载96
+				//String ip = null;
+				int port = -1;
+				//boolean recvonly = false;
+				boolean mediaTransmissionTCP = false;
+				Boolean tcpActive = null;
+				for (Object description : mediaDescriptions) {
+					MediaDescription mediaDescription = (MediaDescription) description;
+					Media media = mediaDescription.getMedia();
+
+					Vector mediaFormats = media.getMediaFormats(false);
+					if (mediaFormats.contains("96")) {
+						port = media.getMediaPort();
+						//String mediaType = media.getMediaType();
+						String protocol = media.getProtocol();
+
+						// 区分TCP发流还是udp, 当前默认udp
+						if ("TCP/RTP/AVP".equals(protocol)) {
+							String setup = mediaDescription.getAttribute("setup");
+							if (setup != null) {
+								mediaTransmissionTCP = true;
+								if ("active".equals(setup)) {
+									tcpActive = true;
+								} else if ("passive".equals(setup)) {
+									tcpActive = false;
+								}
+							}
+						}
+						break;
+					}
+				}
+				if (port == -1) {
+					logger.info("不支持的媒体格式,返回415");
+					// 回复不支持的格式
+					responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
+					return;
+				}
+				String username = sdp.getOrigin().getUsername();
+				String addressStr = sdp.getOrigin().getAddress();
+				//String sessionName = sdp.getSessionName().getValue();
+				logger.info("[上级点播]用户:{}, 地址:{}:{}, ssrc:{}", username, addressStr, port, ssrc);
+				Device device  = null;
+				// 通过 channel 和 gbStream 是否为null 值判断来源是直播流合适国标
+				if (channel != null) {
+					device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId);
+					if (device == null) {
+						logger.warn("点播平台{}的通道{}时未找到设备信息", requesterId, channel);
+						responseAck(evt, Response.SERVER_INTERNAL_ERROR);
+						return;
+					}
+					mediaServerItem = playService.getNewMediaServerItem(device);
+					if (mediaServerItem == null) {
+						logger.warn("未找到可用的zlm");
+						responseAck(evt, Response.BUSY_HERE);
+						return;
+					}
+					SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
+							device.getDeviceId(), channelId,
+							mediaTransmissionTCP);
+					if (tcpActive != null) {
+						sendRtpItem.setTcpActive(tcpActive);
+					}
+					if (sendRtpItem == null) {
+						logger.warn("服务器端口资源不足");
+						responseAck(evt, Response.BUSY_HERE);
+						return;
+					}
+
+					// 写入redis, 超时时回复
+					redisCatchStorage.updateSendRTPSever(sendRtpItem);
+					// 通知下级推流,
+					PlayResult playResult = playService.play(mediaServerItem,device.getDeviceId(), channelId, (mediaServerItemInUSe, responseJSON)->{
+						// 收到推流, 回复200OK, 等待ack
+						// if (sendRtpItem == null) return;
+						sendRtpItem.setStatus(1);
+						redisCatchStorage.updateSendRTPSever(sendRtpItem);
+						// TODO 添加对tcp的支持
+
+						StringBuffer content = new StringBuffer(200);
+						content.append("v=0\r\n");
+						content.append("o="+"00000"+" 0 0 IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");
+						content.append("s=Play\r\n");
+						content.append("c=IN IP4 "+mediaServerItemInUSe.getSdpIp()+"\r\n");
+						content.append("t=0 0\r\n");
+						content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
+						content.append("a=sendonly\r\n");
+						content.append("a=rtpmap:96 PS/90000\r\n");
+						content.append("y="+ ssrc + "\r\n");
+						content.append("f=\r\n");
+
+						try {
+							responseAck(evt, content.toString());
+						} catch (SipException e) {
+							e.printStackTrace();
+						} catch (InvalidArgumentException e) {
+							e.printStackTrace();
+						} catch (ParseException e) {
+							e.printStackTrace();
+						}
+					} ,((event) -> {
+						// 未知错误。直接转发设备点播的错误
+						Response response = null;
+						try {
+							response = getMessageFactory().createResponse(event.getResponse().getStatusCode(), evt.getRequest());
+							ServerTransaction serverTransaction = getServerTransaction(evt);
+							serverTransaction.sendResponse(response);
+							if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
+						} catch (ParseException | SipException | InvalidArgumentException e) {
+							e.printStackTrace();
+						}
+					}));
+					if (logger.isDebugEnabled()) {
+						logger.debug(playResult.getResult().toString());
+					}
+
+				}else if (gbStream != null) {
+					SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(mediaServerItem, addressStr, port, ssrc, requesterId,
+							gbStream.getApp(), gbStream.getStream(), channelId,
+							mediaTransmissionTCP);
+
+					if (tcpActive != null) {
+						sendRtpItem.setTcpActive(tcpActive);
+					}
+					if (sendRtpItem == null) {
+						logger.warn("服务器端口资源不足");
+						responseAck(evt, Response.BUSY_HERE);
+						return;
+					}
+
+					// 写入redis, 超时时回复
+					redisCatchStorage.updateSendRTPSever(sendRtpItem);
+
+					sendRtpItem.setStatus(1);
+					redisCatchStorage.updateSendRTPSever(sendRtpItem);
+					// TODO 添加对tcp的支持
+					StringBuffer content = new StringBuffer(200);
+					content.append("v=0\r\n");
+					content.append("o="+"00000"+" 0 0 IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
+					content.append("s=Play\r\n");
+					content.append("c=IN IP4 "+mediaServerItem.getSdpIp()+"\r\n");
+					content.append("t=0 0\r\n");
+					content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
+					content.append("a=sendonly\r\n");
+					content.append("a=rtpmap:96 PS/90000\r\n");
+					content.append("y="+ ssrc + "\r\n");
+					content.append("f=\r\n");
+
+					try {
+						responseAck(evt, content.toString());
+					} catch (SipException e) {
+						e.printStackTrace();
+					} catch (InvalidArgumentException e) {
+						e.printStackTrace();
+					} catch (ParseException e) {
+						e.printStackTrace();
+					}
+				}
+
+			} else {
+				// 非上级平台请求,查询是否设备请求(通常为接收语音广播的设备)
+				Device device = storager.queryVideoDevice(requesterId);
+				if (device != null) {
+					logger.info("收到设备" + requesterId + "的语音广播Invite请求");
+					responseAck(evt, Response.TRYING);
+
+					String contentString = new String(request.getRawContent());
+					// jainSip不支持y=字段, 移除移除以解析。
+					String substring = contentString;
+					String ssrc = "0000000404";
+					int ssrcIndex = contentString.indexOf("y=");
+					if (ssrcIndex > 0) {
+						substring = contentString.substring(0, ssrcIndex);
+						ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
+					}
+					ssrcIndex = substring.indexOf("f=");
+					if (ssrcIndex > 0) {
+						substring = contentString.substring(0, ssrcIndex);
+					}
+					SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
+
+					//  获取支持的格式
+					Vector mediaDescriptions = sdp.getMediaDescriptions(true);
+					// 查看是否支持PS 负载96
+					int port = -1;
+					//boolean recvonly = false;
+					boolean mediaTransmissionTCP = false;
+					Boolean tcpActive = null;
+					for (int i = 0; i < mediaDescriptions.size(); i++) {
+						MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i);
+						Media media = mediaDescription.getMedia();
+
+						Vector mediaFormats = media.getMediaFormats(false);
+						if (mediaFormats.contains("8")) {
+							port = media.getMediaPort();
+							String protocol = media.getProtocol();
+							// 区分TCP发流还是udp, 当前默认udp
+							if ("TCP/RTP/AVP".equals(protocol)) {
+								String setup = mediaDescription.getAttribute("setup");
+								if (setup != null) {
+									mediaTransmissionTCP = true;
+									if ("active".equals(setup)) {
+										tcpActive = true;
+									} else if ("passive".equals(setup)) {
+										tcpActive = false;
+									}
+								}
+							}
+							break;
+						}
+					}
+					if (port == -1) {
+						logger.info("不支持的媒体格式,返回415");
+						// 回复不支持的格式
+						responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 不支持的格式,发415
+						return;
+					}
+					String username = sdp.getOrigin().getUsername();
+					String addressStr = sdp.getOrigin().getAddress();
+					logger.info("设备{}请求语音流,地址:{}:{},ssrc:{}", username, addressStr, port, ssrc);
+
+				} else {
+					logger.warn("来自无效设备/平台的请求");
+					responseAck(evt, Response.BAD_REQUEST);
+				}
+			}
+
+		} catch (SipException | InvalidArgumentException | ParseException e) {
+			e.printStackTrace();
+			logger.warn("sdp解析错误");
+			e.printStackTrace();
+		} catch (SdpParseException e) {
+			e.printStackTrace();
+		} catch (SdpException e) {
+			e.printStackTrace();
+		}
+	}
+
+
+	/***
+	 * 回复状态码
+	 * 100 trying
+	 * 200 OK
+	 * 400
+	 * 404
+	 * @param evt
+	 * @throws SipException
+	 * @throws InvalidArgumentException
+	 * @throws ParseException
+	 */
+	private void responseAck(RequestEvent evt, int statusCode) throws SipException, InvalidArgumentException, ParseException {
+		Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
+		ServerTransaction serverTransaction = getServerTransaction(evt);
+		serverTransaction.sendResponse(response);
+		if (statusCode >= 200) {
+			if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
+		}
+	}
+
+	private void responseAck(RequestEvent evt, int statusCode, String msg) throws SipException, InvalidArgumentException, ParseException {
+		Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
+		response.setReasonPhrase(msg);
+		ServerTransaction serverTransaction = getServerTransaction(evt);
+		serverTransaction.sendResponse(response);
+		if (statusCode >= 200) {
+			if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
+		}
+	}
+
+	/**
+	 * 回复带sdp的200
+	 * @param evt
+	 * @param sdp
+	 * @throws SipException
+	 * @throws InvalidArgumentException
+	 * @throws ParseException
+	 */
+	private void responseAck(RequestEvent evt, String sdp) throws SipException, InvalidArgumentException, ParseException {
+		Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
+		SipFactory sipFactory = SipFactory.getInstance();
+		ContentTypeHeader contentTypeHeader = sipFactory.createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
+		response.setContent(sdp, contentTypeHeader);
+
+		SipURI sipURI = (SipURI)evt.getRequest().getRequestURI();
+
+		Address concatAddress = sipFactory.createAddressFactory().createAddress(
+				sipFactory.createAddressFactory().createSipURI(sipURI.getUser(),  sipURI.getHost()+":"+sipURI.getPort()
+				));
+		response.addHeader(sipFactory.createHeaderFactory().createContactHeader(concatAddress));
+		getServerTransaction(evt).sendResponse(response);
+	}
+
+
+
+
+
+
+	public SIPCommanderFroPlatform getCmderFroPlatform() {
+		return cmderFroPlatform;
+	}
+
+	public void setCmderFroPlatform(SIPCommanderFroPlatform cmderFroPlatform) {
+		this.cmderFroPlatform = cmderFroPlatform;
+	}
+
+	public IVideoManagerStorager getStorager() {
+		return storager;
+	}
+
+	public void setStorager(IVideoManagerStorager storager) {
+		this.storager = storager;
+	}
+
+	public SIPCommander getCmder() {
+		return cmder;
+	}
+
+	public void setCmder(SIPCommander cmder) {
+		this.cmder = cmder;
+	}
+
+	public IPlayService getPlayService() {
+		return playService;
+	}
+
+	public void setPlayService(IPlayService playService) {
+		this.playService = playService;
+	}
+
+	public IRedisCatchStorage getRedisCatchStorage() {
+		return redisCatchStorage;
+	}
+
+	public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) {
+		this.redisCatchStorage = redisCatchStorage;
+	}
+
+	public IMediaServerService getMediaServerService() {
+		return mediaServerService;
+	}
+
+	public void setMediaServerService(IMediaServerService mediaServerService) {
+		this.mediaServerService = mediaServerService;
+	}
+}

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1083 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java


+ 394 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/NotifyRequestProcessor.java

@@ -0,0 +1,394 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.request.impl;
+
+import java.io.ByteArrayInputStream;
+import java.text.ParseException;
+import java.util.Iterator;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.ServerTransaction;
+import javax.sip.SipException;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+
+import com.zd.alg.iot.vmp.conf.UserSetup;
+import com.zd.alg.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
+import com.zd.alg.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
+import com.zd.alg.iot.vmp.storager.IRedisCatchStorage;
+import com.zd.alg.iot.vmp.storager.IVideoManagerStorager;
+import com.zd.alg.iot.vmp.common.VideoManagerConstants;
+import com.zd.alg.iot.vmp.gb28181.bean.BaiduPoint;
+import com.zd.alg.iot.vmp.gb28181.bean.Device;
+import com.zd.alg.iot.vmp.gb28181.bean.DeviceAlarm;
+import com.zd.alg.iot.vmp.gb28181.bean.DeviceChannel;
+import com.zd.alg.iot.vmp.gb28181.bean.MobilePosition;
+import com.zd.alg.iot.vmp.gb28181.event.DeviceOffLineDetector;
+import com.zd.alg.iot.vmp.gb28181.event.EventPublisher;
+import com.zd.alg.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import com.zd.alg.iot.vmp.gb28181.utils.NumericUtil;
+import com.zd.alg.iot.vmp.gb28181.utils.XmlUtil;
+import com.zd.alg.iot.vmp.utils.GpsUtil;
+import com.zd.alg.iot.vmp.utils.SpringBeanFactory;
+import com.zd.alg.iot.vmp.utils.redis.RedisUtil;
+
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+
+/**
+ * @Description: Notify请求处理器
+ * @author: lawrencehj
+ * @date: 2021年1月27日
+ */
+
+public class NotifyRequestProcessor extends SIPRequestAbstractProcessor {
+
+	private UserSetup userSetup = (UserSetup) SpringBeanFactory.getBean("userSetup");
+
+    private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class);
+
+	private IVideoManagerStorager storager;
+
+	private IRedisCatchStorage redisCatchStorage;
+
+	private EventPublisher publisher;
+
+	private DeviceOffLineDetector offLineDetector;
+
+	private static final String NOTIFY_CATALOG = "Catalog";
+	private static final String NOTIFY_ALARM = "Alarm";
+	private static final String NOTIFY_MOBILE_POSITION = "MobilePosition";
+
+	@Override
+	public void process(RequestEvent evt) {
+		try {
+			Element rootElement = getRootElement(evt);
+			String cmd = XmlUtil.getText(rootElement, "CmdType");
+
+			if (NOTIFY_CATALOG.equals(cmd)) {
+				logger.info("接收到Catalog通知");
+				processNotifyCatalogList(evt);
+			} else if (NOTIFY_ALARM.equals(cmd)) {
+				logger.info("接收到Alarm通知");
+				processNotifyAlarm(evt);
+			} else if (NOTIFY_MOBILE_POSITION.equals(cmd)) {
+				logger.info("接收到MobilePosition通知");
+				processNotifyMobilePosition(evt);
+			} else {
+				logger.info("接收到消息:" + cmd);
+				response200Ok(evt);
+			}
+		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * 处理MobilePosition移动位置Notify
+	 *
+	 * @param evt
+	 */
+	private void processNotifyMobilePosition(RequestEvent evt) {
+		try {
+			// 回复 200 OK
+			Element rootElement = getRootElement(evt);
+			MobilePosition mobilePosition = new MobilePosition();
+			Element deviceIdElement = rootElement.element("DeviceID");
+			String deviceId = deviceIdElement.getTextTrim().toString();
+			Device device = storager.queryVideoDevice(deviceId);
+			if (device != null) {
+				if (!StringUtils.isEmpty(device.getName())) {
+					mobilePosition.setDeviceName(device.getName());
+				}
+			}
+			mobilePosition.setDeviceId(XmlUtil.getText(rootElement, "DeviceID"));
+			mobilePosition.setTime(XmlUtil.getText(rootElement, "Time"));
+			mobilePosition.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude")));
+			mobilePosition.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude")));
+			if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Speed"))) {
+				mobilePosition.setSpeed(Double.parseDouble(XmlUtil.getText(rootElement, "Speed")));
+			} else {
+				mobilePosition.setSpeed(0.0);
+			}
+			if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Direction"))) {
+				mobilePosition.setDirection(Double.parseDouble(XmlUtil.getText(rootElement, "Direction")));
+			} else {
+				mobilePosition.setDirection(0.0);
+			}
+			if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Altitude"))) {
+				mobilePosition.setAltitude(Double.parseDouble(XmlUtil.getText(rootElement, "Altitude")));
+			} else {
+				mobilePosition.setAltitude(0.0);
+			}
+			mobilePosition.setReportSource("Mobile Position");
+			BaiduPoint bp = new BaiduPoint();
+			bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude()));
+			logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat());
+			mobilePosition.setGeodeticSystem("BD-09");
+			mobilePosition.setCnLng(bp.getBdLng());
+			mobilePosition.setCnLat(bp.getBdLat());
+			if (!userSetup.getSavePositionHistory()) {
+				storager.clearMobilePositionsByDeviceId(deviceId);
+			}
+			storager.insertMobilePosition(mobilePosition);
+			response200Ok(evt);
+		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/***
+	 * 处理alarm设备报警Notify
+	 *
+	 * @param evt
+	 */
+	private void processNotifyAlarm(RequestEvent evt) {
+		try {
+			Element rootElement = getRootElement(evt);
+			Element deviceIdElement = rootElement.element("DeviceID");
+			String deviceId = deviceIdElement.getText().toString();
+
+			Device device = storager.queryVideoDevice(deviceId);
+			if (device == null) {
+				return;
+			}
+			rootElement = getRootElement(evt, device.getCharset());
+			DeviceAlarm deviceAlarm = new DeviceAlarm();
+			deviceAlarm.setDeviceId(deviceId);
+			deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority"));
+			deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod"));
+			deviceAlarm.setAlarmTime(XmlUtil.getText(rootElement, "AlarmTime"));
+			if (XmlUtil.getText(rootElement, "AlarmDescription") == null) {
+				deviceAlarm.setAlarmDescription("");
+			} else {
+				deviceAlarm.setAlarmDescription(XmlUtil.getText(rootElement, "AlarmDescription"));
+			}
+			if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Longitude"))) {
+				deviceAlarm.setLongitude(Double.parseDouble(XmlUtil.getText(rootElement, "Longitude")));
+			} else {
+				deviceAlarm.setLongitude(0.00);
+			}
+			if (NumericUtil.isDouble(XmlUtil.getText(rootElement, "Latitude"))) {
+				deviceAlarm.setLatitude(Double.parseDouble(XmlUtil.getText(rootElement, "Latitude")));
+			} else {
+				deviceAlarm.setLatitude(0.00);
+			}
+
+			if (deviceAlarm.getAlarmMethod().equals("4")) {
+				MobilePosition mobilePosition = new MobilePosition();
+				mobilePosition.setDeviceId(deviceAlarm.getDeviceId());
+				mobilePosition.setTime(deviceAlarm.getAlarmTime());
+				mobilePosition.setLongitude(deviceAlarm.getLongitude());
+				mobilePosition.setLatitude(deviceAlarm.getLatitude());
+				mobilePosition.setReportSource("GPS Alarm");
+				BaiduPoint bp = new BaiduPoint();
+				bp = GpsUtil.Wgs84ToBd09(String.valueOf(mobilePosition.getLongitude()), String.valueOf(mobilePosition.getLatitude()));
+				logger.info("百度坐标:" + bp.getBdLng() + ", " + bp.getBdLat());
+				mobilePosition.setGeodeticSystem("BD-09");
+				mobilePosition.setCnLng(bp.getBdLng());
+				mobilePosition.setCnLat(bp.getBdLat());
+				if (!userSetup.getSavePositionHistory()) {
+					storager.clearMobilePositionsByDeviceId(deviceId);
+				}
+				storager.insertMobilePosition(mobilePosition);
+			}
+			// TODO: 需要实现存储报警信息、报警分类
+
+			// 回复200 OK
+			response200Ok(evt);
+			if (offLineDetector.isOnline(deviceId)) {
+				publisher.deviceAlarmEventPublish(deviceAlarm);
+			}
+		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/***
+	 * 处理catalog设备目录列表Notify
+	 *
+	 * @param evt
+	 */
+	private void processNotifyCatalogList(RequestEvent evt) {
+		try {
+			Element rootElement = getRootElement(evt);
+			Element deviceIdElement = rootElement.element("DeviceID");
+			String deviceId = deviceIdElement.getText();
+			Device device = storager.queryVideoDevice(deviceId);
+			if (device != null ) {
+				rootElement = getRootElement(evt, device.getCharset());
+			}
+			Element deviceListElement = rootElement.element("DeviceList");
+			if (deviceListElement == null) {
+				return;
+			}
+			Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
+			if (deviceListIterator != null) {
+				if (device == null) {
+					return;
+				}
+				// 遍历DeviceList
+				while (deviceListIterator.hasNext()) {
+					Element itemDevice = deviceListIterator.next();
+					Element channelDeviceElement = itemDevice.element("DeviceID");
+					if (channelDeviceElement == null) {
+						continue;
+					}
+					String channelDeviceId = channelDeviceElement.getTextTrim();
+					Element channdelNameElement = itemDevice.element("Name");
+					String channelName = channdelNameElement != null ? channdelNameElement.getTextTrim().toString() : "";
+					Element statusElement = itemDevice.element("Status");
+					String status = statusElement != null ? statusElement.getTextTrim().toString() : "ON";
+					DeviceChannel deviceChannel = new DeviceChannel();
+					deviceChannel.setName(channelName);
+					deviceChannel.setChannelId(channelDeviceId);
+					// ONLINE OFFLINE HIKVISION DS-7716N-E4 NVR的兼容性处理
+					if (status.equals("ON") || status.equals("On") || status.equals("ONLINE")) {
+						deviceChannel.setStatus(1);
+					}
+					if (status.equals("OFF") || status.equals("Off") || status.equals("OFFLINE")) {
+						deviceChannel.setStatus(0);
+					}
+
+					deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer"));
+					deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model"));
+					deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner"));
+					deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode"));
+					deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block"));
+					deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address"));
+					if (XmlUtil.getText(itemDevice, "Parental") == null
+							|| XmlUtil.getText(itemDevice, "Parental") == "") {
+						deviceChannel.setParental(0);
+					} else {
+						deviceChannel.setParental(Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")));
+					}
+					deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID"));
+					if (XmlUtil.getText(itemDevice, "SafetyWay") == null
+							|| XmlUtil.getText(itemDevice, "SafetyWay") == "") {
+						deviceChannel.setSafetyWay(0);
+					} else {
+						deviceChannel.setSafetyWay(Integer.parseInt(XmlUtil.getText(itemDevice, "SafetyWay")));
+					}
+					if (XmlUtil.getText(itemDevice, "RegisterWay") == null
+							|| XmlUtil.getText(itemDevice, "RegisterWay") == "") {
+						deviceChannel.setRegisterWay(1);
+					} else {
+						deviceChannel.setRegisterWay(Integer.parseInt(XmlUtil.getText(itemDevice, "RegisterWay")));
+					}
+					deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum"));
+					if (XmlUtil.getText(itemDevice, "Certifiable") == null
+							|| XmlUtil.getText(itemDevice, "Certifiable") == "") {
+						deviceChannel.setCertifiable(0);
+					} else {
+						deviceChannel.setCertifiable(Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable")));
+					}
+					if (XmlUtil.getText(itemDevice, "ErrCode") == null
+							|| XmlUtil.getText(itemDevice, "ErrCode") == "") {
+						deviceChannel.setErrCode(0);
+					} else {
+						deviceChannel.setErrCode(Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode")));
+					}
+					deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime"));
+					deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy"));
+					deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress"));
+					if (XmlUtil.getText(itemDevice, "Port") == null || XmlUtil.getText(itemDevice, "Port") == "") {
+						deviceChannel.setPort(0);
+					} else {
+						deviceChannel.setPort(Integer.parseInt(XmlUtil.getText(itemDevice, "Port")));
+					}
+					deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password"));
+					if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Longitude"))) {
+						deviceChannel.setLongitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude")));
+					} else {
+						deviceChannel.setLongitude(0.00);
+					}
+					if (NumericUtil.isDouble(XmlUtil.getText(itemDevice, "Latitude"))) {
+						deviceChannel.setLatitude(Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude")));
+					} else {
+						deviceChannel.setLatitude(0.00);
+					}
+					if (XmlUtil.getText(itemDevice, "PTZType") == null
+							|| XmlUtil.getText(itemDevice, "PTZType") == "") {
+						deviceChannel.setPTZType(0);
+					} else {
+						deviceChannel.setPTZType(Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType")));
+					}
+					deviceChannel.setHasAudio(true); // 默认含有音频,播放时再检查是否有音频及是否AAC
+					storager.updateChannel(device.getDeviceId(), deviceChannel);
+				}
+
+				// RequestMessage msg = new RequestMessage();
+				// msg.setDeviceId(deviceId);
+				// msg.setType(DeferredResultHolder.CALLBACK_CMD_CATALOG);
+				// msg.setData(device);
+				// deferredResultHolder.invokeResult(msg);
+				// 回复200 OK
+				response200Ok(evt);
+				if (offLineDetector.isOnline(deviceId)) {
+					publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_MESSAGE);
+				}
+			}
+		} catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
+			e.printStackTrace();
+		}
+	}
+
+	/***
+	 * 回复200 OK
+	 *
+	 * @param evt
+	 * @throws SipException
+	 * @throws InvalidArgumentException
+	 * @throws ParseException
+	 */
+	private void response200Ok(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException {
+		Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
+		ServerTransaction serverTransaction = getServerTransaction(evt);
+		serverTransaction.sendResponse(response);
+		if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
+	}
+	private Element getRootElement(RequestEvent evt) throws DocumentException {
+		return getRootElement(evt, "gb2312");
+	}
+	private Element getRootElement(RequestEvent evt, String charset) throws DocumentException {
+		if (charset == null) charset = "gb2312";
+		Request request = evt.getRequest();
+		SAXReader reader = new SAXReader();
+		reader.setEncoding(charset);
+		Document xml = reader.read(new ByteArrayInputStream(request.getRawContent()));
+		return xml.getRootElement();
+	}
+
+	public void setCmder(SIPCommander cmder) {
+	}
+
+	public void setStorager(IVideoManagerStorager storager) {
+		this.storager = storager;
+	}
+
+	public void setPublisher(EventPublisher publisher) {
+		this.publisher = publisher;
+	}
+
+	public void setRedis(RedisUtil redis) {
+	}
+
+	public void setDeferredResultHolder(DeferredResultHolder deferredResultHolder) {
+	}
+
+	public void setOffLineDetector(DeviceOffLineDetector offLineDetector) {
+		this.offLineDetector = offLineDetector;
+	}
+
+	public IRedisCatchStorage getRedisCatchStorage() {
+		return redisCatchStorage;
+	}
+
+	public void setRedisCatchStorage(IRedisCatchStorage redisCatchStorage) {
+		this.redisCatchStorage = redisCatchStorage;
+	}
+}

+ 31 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/OtherRequestProcessor.java

@@ -0,0 +1,31 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.request.impl;
+
+import javax.sip.RequestEvent;
+
+import com.zd.alg.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @Description:暂不支持的消息请求处理器
+ * @author: dgs
+ * @date:   2020年5月3日 下午5:32:59
+ */
+public class OtherRequestProcessor extends SIPRequestAbstractProcessor {
+
+	private Logger logger = LoggerFactory.getLogger(OtherRequestProcessor.class);
+
+	/**
+	 * <p>Title: process</p>
+	 * <p>Description: </p>
+	 * @param evt
+	 * @param layer
+	 * @param transaction
+	 * @param config
+	 */
+	@Override
+	public void process(RequestEvent evt) {
+		logger.info("Unsupported the method: " + evt.getRequest().getMethod());
+	}
+
+}

+ 193 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/RegisterRequestProcessor.java

@@ -0,0 +1,193 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.request.impl;
+
+import com.zd.alg.iot.vmp.conf.SipConfig;
+import com.zd.alg.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
+import com.zd.alg.iot.vmp.storager.IVideoManagerStorager;
+import com.zd.alg.iot.vmp.common.VideoManagerConstants;
+import com.zd.alg.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
+import com.zd.alg.iot.vmp.gb28181.auth.RegisterLogicHandler;
+import com.zd.alg.iot.vmp.gb28181.bean.Device;
+import com.zd.alg.iot.vmp.gb28181.bean.WvpSipDate;
+import com.zd.alg.iot.vmp.gb28181.event.EventPublisher;
+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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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.*;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+import java.security.NoSuchAlgorithmException;
+import java.text.ParseException;
+import java.util.Calendar;
+import java.util.Locale;
+
+/**
+ * @Description:收到注册请求 处理
+ * @author: dgs
+ * @date:   2020年5月3日 下午4:47:25
+ */
+public class RegisterRequestProcessor extends SIPRequestAbstractProcessor {
+
+	private Logger logger = LoggerFactory.getLogger(RegisterRequestProcessor.class);
+
+	private SipConfig sipConfig;
+
+	private RegisterLogicHandler handler;
+
+	private IVideoManagerStorager storager;
+
+	private EventPublisher publisher;
+
+	/**
+	 * 收到注册请求 处理
+ 	 * @param evt
+	 */
+	@Override
+	public void process(RequestEvent evt) {
+		try {
+			RequestEventExt evtExt = (RequestEventExt)evt;
+			String requestAddress = evtExt.getRemoteIpAddress() + ":" + evtExt.getRemotePort();
+			logger.info("[{}] 收到注册请求,开始处理", requestAddress);
+			Request request = evt.getRequest();
+
+			Response response = null;
+			boolean passwordCorrect = false;
+			// 注册标志  0:未携带授权头或者密码错误  1:注册成功   2:注销成功
+			int registerFlag = 0;
+			FromHeader fromHeader = (FromHeader) request.getHeader(FromHeader.NAME);
+			AddressImpl address = (AddressImpl) fromHeader.getAddress();
+			SipUri uri = (SipUri) address.getURI();
+			String deviceId = uri.getUser();
+			Device device = storager.queryVideoDevice(deviceId);
+			AuthorizationHeader authorhead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
+			// 校验密码是否正确
+			if (authorhead != null) {
+				passwordCorrect = new DigestServerAuthenticationHelper().doAuthenticatePlainTextPassword(request,
+						sipConfig.getPassword());
+			}
+			if (StringUtils.isEmpty(sipConfig.getPassword())){
+				passwordCorrect = true;
+			}
+
+			// 未携带授权头或者密码错误 回复401
+			if (authorhead == null ) {
+
+				logger.info("[{}] 未携带授权头 回复401", requestAddress);
+				response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request);
+				new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain());
+			}else {
+				if (!passwordCorrect){
+					// 注册失败
+					response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
+					response.setReasonPhrase("wrong password");
+					logger.info("[{}] 密码错误 回复403", requestAddress);
+				}else {
+					// 携带授权头并且密码正确
+					response = getMessageFactory().createResponse(Response.OK, request);
+					// 添加date头
+					SIPDateHeader dateHeader = new SIPDateHeader();
+					// 使用自己修改的
+					WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
+					dateHeader.setDate(wvpSipDate);
+					response.addHeader(dateHeader);
+
+					ExpiresHeader expiresHeader = (ExpiresHeader) request.getHeader(Expires.NAME);
+					if (expiresHeader == null) {
+						response = getMessageFactory().createResponse(Response.BAD_REQUEST, request);
+						ServerTransaction serverTransaction = getServerTransaction(evt);
+						serverTransaction.sendResponse(response);
+						if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
+						return;
+					}
+					// 添加Contact头
+					response.addHeader(request.getHeader(ContactHeader.NAME));
+					// 添加Expires头
+					response.addHeader(request.getExpires());
+
+					// 获取到通信地址等信息
+					ViaHeader viaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
+					String received = viaHeader.getReceived();
+					int rPort = viaHeader.getRPort();
+					// 解析本地地址替代
+					if (StringUtils.isEmpty(received) || rPort == -1) {
+						received = viaHeader.getHost();
+						rPort = viaHeader.getPort();
+					}
+					//
+					if (device == null) {
+						device = new Device();
+						device.setStreamMode("UDP");
+						device.setCharset("gb2312");
+						device.setDeviceId(deviceId);
+						device.setFirsRegister(true);
+					}
+					device.setIp(received);
+					device.setPort(rPort);
+					device.setHostAddress(received.concat(":").concat(String.valueOf(rPort)));
+					// 注销成功
+					if (expiresHeader.getExpires() == 0) {
+						registerFlag = 2;
+					}
+					// 注册成功
+					else {
+						device.setExpires(expiresHeader.getExpires());
+						registerFlag = 1;
+						// 判断TCP还是UDP
+						boolean isTcp = false;
+						ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
+						String transport = reqViaHeader.getTransport();
+						if (transport.equals("TCP")) {
+							isTcp = true;
+						}
+						device.setTransport(isTcp ? "TCP" : "UDP");
+					}
+				}
+			}
+
+			ServerTransaction serverTransaction = getServerTransaction(evt);
+			serverTransaction.sendResponse(response);
+			if (serverTransaction.getDialog() != null) serverTransaction.getDialog().delete();
+			// 注册成功
+			// 保存到redis
+			// 下发catelog查询目录
+			if (registerFlag == 1 ) {
+				logger.info("[{}] 注册成功! deviceId:" + device.getDeviceId(), requestAddress);
+				// 重新注册更新设备和通道,以免设备替换或更新后信息无法更新
+				handler.onRegister(device);
+				publisher.onlineEventPublish(device, VideoManagerConstants.EVENT_ONLINE_REGISTER);
+			} else if (registerFlag == 2) {
+				logger.info("[{}] 注销成功! deviceId:" + device.getDeviceId(), requestAddress);
+				publisher.outlineEventPublish(device.getDeviceId(), VideoManagerConstants.EVENT_OUTLINE_UNREGISTER);
+			}
+		} catch (SipException | InvalidArgumentException | NoSuchAlgorithmException | ParseException e) {
+			e.printStackTrace();
+		}
+
+	}
+
+	public void setSipConfig(SipConfig sipConfig) {
+		this.sipConfig = sipConfig;
+	}
+
+	public void setHandler(RegisterLogicHandler handler) {
+		this.handler = handler;
+	}
+
+	public void setVideoManagerStorager(IVideoManagerStorager storager) {
+		this.storager = storager;
+	}
+
+	public void setPublisher(EventPublisher publisher) {
+		this.publisher = publisher;
+	}
+
+}

+ 62 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/request/impl/SubscribeRequestProcessor.java

@@ -0,0 +1,62 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.request.impl;
+
+import java.text.ParseException;
+
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.ServerTransaction;
+import javax.sip.SipException;
+import javax.sip.header.ExpiresHeader;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+
+import com.zd.alg.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @Description:SUBSCRIBE请求处理器
+ * @author: dgs
+ * @date:   2020年5月3日 下午5:31:20
+ */
+public class SubscribeRequestProcessor extends SIPRequestAbstractProcessor {
+
+	private Logger logger = LoggerFactory.getLogger(SubscribeRequestProcessor.class);
+
+	/**
+	 * 处理SUBSCRIBE请求
+	 *
+	 * @param evt
+	 */
+	@Override
+	public void process(RequestEvent evt) {
+		Request request = evt.getRequest();
+
+		try {
+			Response response = null;
+			response = getMessageFactory().createResponse(200, request);
+			if (response != null) {
+				ExpiresHeader expireHeader = getHeaderFactory().createExpiresHeader(30);
+				response.setExpires(expireHeader);
+			}
+			logger.info("response : " + response.toString());
+			ServerTransaction transaction = getServerTransaction(evt);
+			if (transaction != null) {
+				transaction.sendResponse(response);
+				transaction.getDialog().delete();
+				transaction.terminate();
+			} else {
+				logger.info("processRequest serverTransactionId is null.");
+			}
+
+		} catch (ParseException e) {
+			e.printStackTrace();
+		} catch (SipException e) {
+			e.printStackTrace();
+		} catch (InvalidArgumentException e) {
+			e.printStackTrace();
+		}
+
+	}
+
+}

+ 19 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/response/ISIPResponseProcessor.java

@@ -0,0 +1,19 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.response;
+
+import java.text.ParseException;
+
+import javax.sip.ResponseEvent;
+
+import com.zd.alg.iot.vmp.conf.SipConfig;
+import com.zd.alg.iot.vmp.gb28181.SipLayer;
+
+/**
+ * @Description:处理接收IPCamera发来的SIP协议响应消息
+ * @author: dgs
+ * @date:   2020年5月3日 下午4:42:22
+ */
+public interface ISIPResponseProcessor {
+
+	public void process(ResponseEvent evt, SipLayer layer, SipConfig config) throws ParseException;
+
+}

+ 30 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/response/impl/ByeResponseProcessor.java

@@ -0,0 +1,30 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.response.impl;
+
+import javax.sip.ResponseEvent;
+
+import com.zd.alg.iot.vmp.conf.SipConfig;
+import com.zd.alg.iot.vmp.gb28181.SipLayer;
+import com.zd.alg.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Description: BYE请求响应器
+ * @author: dgs
+ * @date:   2020年5月3日 下午5:32:05
+ */
+@Component
+public class ByeResponseProcessor implements ISIPResponseProcessor {
+
+	/**
+	 * 处理BYE响应
+	 *
+	 * @param evt
+	 * @param layer
+	 * @param config
+	 */
+	@Override
+	public void process(ResponseEvent evt, SipLayer layer, SipConfig config) {
+		// TODO Auto-generated method stub
+	}
+
+}

+ 32 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/response/impl/CancelResponseProcessor.java

@@ -0,0 +1,32 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.response.impl;
+
+import javax.sip.ResponseEvent;
+
+import com.zd.alg.iot.vmp.conf.SipConfig;
+import com.zd.alg.iot.vmp.gb28181.SipLayer;
+import com.zd.alg.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Description:CANCEL响应处理器
+ * @author: dgs
+ * @date:   2020年5月3日 下午5:32:23
+ */
+@Component
+public class CancelResponseProcessor implements ISIPResponseProcessor {
+
+	/**
+	 * 处理CANCEL响应
+	 *
+	 * @param evt
+	 * @param layer
+	 * @param transaction
+	 * @param config
+	 */
+	@Override
+	public void process(ResponseEvent evt, SipLayer layer, SipConfig config) {
+		// TODO Auto-generated method stub
+
+	}
+
+}

+ 74 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/response/impl/InviteResponseProcessor.java

@@ -0,0 +1,74 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.response.impl;
+
+import java.text.ParseException;
+
+import javax.sip.*;
+import javax.sip.address.SipURI;
+import javax.sip.header.CSeqHeader;
+import javax.sip.message.Request;
+import javax.sip.message.Response;
+
+import com.zd.alg.iot.vmp.conf.SipConfig;
+import com.zd.alg.iot.vmp.gb28181.SipLayer;
+import com.zd.alg.iot.vmp.gb28181.session.VideoStreamSessionManager;
+import com.zd.alg.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
+import gov.nist.javax.sip.ResponseEventExt;
+import gov.nist.javax.sip.stack.SIPDialog;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+
+/**
+ * @Description:处理INVITE响应
+ * @author: dgs
+ * @date: 2020年5月3日 下午4:43:52
+ */
+@Component
+public class InviteResponseProcessor implements ISIPResponseProcessor {
+
+	 private final static Logger logger = LoggerFactory.getLogger(InviteResponseProcessor.class);
+
+	@Autowired
+	private VideoStreamSessionManager streamSession;
+
+	/**
+	 * 处理invite响应
+	 *
+	 * @param evt 响应消息
+	 * @throws ParseException
+	 */
+	@Override
+	public void process(ResponseEvent evt, SipLayer layer, SipConfig config) throws ParseException {
+		try {
+			Response response = evt.getResponse();
+			int statusCode = response.getStatusCode();
+			// trying不会回复
+			if (statusCode == Response.TRYING) {
+			}
+			// 成功响应
+			// 下发ack
+			if (statusCode == Response.OK) {
+				ResponseEventExt event = (ResponseEventExt)evt;
+				SIPDialog dialog = (SIPDialog)evt.getDialog();
+				CSeqHeader cseq = (CSeqHeader) response.getHeader(CSeqHeader.NAME);
+				Request reqAck = dialog.createAck(cseq.getSeqNumber());
+				SipURI requestURI = (SipURI) reqAck.getRequestURI();
+				requestURI.setHost(event.getRemoteIpAddress());
+				requestURI.setPort(event.getRemotePort());
+				reqAck.setRequestURI(requestURI);
+				logger.info("向 " + event.getRemoteIpAddress() + ":" + event.getRemotePort() + "回复ack");
+				SipURI sipURI = (SipURI)dialog.getRemoteParty().getURI();
+				String deviceId = requestURI.getUser();
+				String channelId = sipURI.getUser();
+
+				dialog.sendAck(reqAck);
+
+			}
+		} catch (InvalidArgumentException | SipException e) {
+			e.printStackTrace();
+		}
+	}
+
+}

+ 31 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/response/impl/OtherResponseProcessor.java

@@ -0,0 +1,31 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.response.impl;
+
+import javax.sip.ResponseEvent;
+
+import com.zd.alg.iot.vmp.conf.SipConfig;
+import com.zd.alg.iot.vmp.gb28181.SipLayer;
+import com.zd.alg.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Description:暂不支持的消息响应处理器
+ * @author: dgs
+ * @date:   2020年5月3日 下午5:32:59
+ */
+@Component
+public class OtherResponseProcessor implements ISIPResponseProcessor {
+
+	/**
+	 * <p>Title: process</p>
+	 * <p>Description: </p>
+	 * @param evt
+	 * @param layer
+	 * @param config
+	 */
+	@Override
+	public void process(ResponseEvent evt, SipLayer layer, SipConfig config) {
+		// TODO Auto-generated method stub
+
+	}
+
+}

+ 100 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/transmit/response/impl/RegisterResponseProcessor.java

@@ -0,0 +1,100 @@
+package com.zd.alg.iot.vmp.gb28181.transmit.response.impl;
+
+import com.zd.alg.iot.vmp.conf.SipConfig;
+import com.zd.alg.iot.vmp.gb28181.SipLayer;
+import com.zd.alg.iot.vmp.storager.IRedisCatchStorage;
+import com.zd.alg.iot.vmp.storager.IVideoManagerStorager;
+import com.zd.alg.iot.vmp.gb28181.bean.ParentPlatform;
+import com.zd.alg.iot.vmp.gb28181.bean.ParentPlatformCatch;
+import com.zd.alg.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
+import com.zd.alg.iot.vmp.gb28181.transmit.response.ISIPResponseProcessor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import javax.sip.ResponseEvent;
+import javax.sip.header.CallIdHeader;
+import javax.sip.header.WWWAuthenticateHeader;
+import javax.sip.message.Response;
+
+/**
+ * @Description:Register响应处理器
+ * @author: dgs
+ * @date:   2020年5月3日 下午5:32:23
+ */
+@Component
+public class RegisterResponseProcessor implements ISIPResponseProcessor {
+
+	private Logger logger = LoggerFactory.getLogger(RegisterResponseProcessor.class);
+
+	@Autowired
+	private ISIPCommanderForPlatform sipCommanderForPlatform;
+
+	@Autowired
+	private IVideoManagerStorager storager;
+
+	@Autowired
+	private IRedisCatchStorage redisCatchStorage;
+
+	public RegisterResponseProcessor() {
+	}
+
+	/**
+	 * 处理Register响应
+	 *
+ 	 * @param evt
+	 * @param layer
+	 * @param config
+	 */
+	@Override
+	public void process(ResponseEvent evt, SipLayer layer, SipConfig config) {
+		Response response = evt.getResponse();
+		CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME);
+		String callId = callIdHeader.getCallId();
+
+		String platformGBId = redisCatchStorage.queryPlatformRegisterInfo(callId);
+		if (platformGBId == null) {
+			logger.info(String.format("未找到callId: %s 的注册/注销平台id", callId ));
+			return;
+		}
+
+		ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(platformGBId);
+		if (parentPlatformCatch == null) {
+			logger.warn(String.format("收到 %s 的注册/注销%S请求, 但是平台缓存信息未查询到!!!", platformGBId, response.getStatusCode()));
+			return;
+		}
+		String action = parentPlatformCatch.getParentPlatform().getExpires().equals("0") ? "注销" : "注册";
+		logger.info(String.format("收到 %s %s的%S响应", platformGBId, action, response.getStatusCode() ));
+		ParentPlatform parentPlatform = parentPlatformCatch.getParentPlatform();
+		if (parentPlatform == null) {
+			logger.warn(String.format("收到 %s %s的%S请求, 但是平台信息未查询到!!!", platformGBId, action, response.getStatusCode()));
+			return;
+		}
+
+		if (response.getStatusCode() == 401) {
+			WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME);
+			sipCommanderForPlatform.register(parentPlatform, callId, www, null, null);
+		}else if (response.getStatusCode() == 200){
+			// 注册/注销成功
+			logger.info(String.format("%s %s成功", platformGBId, action));
+			redisCatchStorage.delPlatformRegisterInfo(callId);
+			parentPlatform.setStatus("注册".equals(action));
+			// 取回Expires设置,避免注销过程中被置为0
+			ParentPlatform parentPlatformTmp = storager.queryParentPlatByServerGBId(platformGBId);
+			String expires = parentPlatformTmp.getExpires();
+			parentPlatform.setExpires(expires);
+			parentPlatform.setId(parentPlatformTmp.getId());
+			storager.updateParentPlatformStatus(platformGBId, "注册".equals(action));
+
+			redisCatchStorage.updatePlatformRegister(parentPlatform);
+
+			redisCatchStorage.updatePlatformKeepalive(parentPlatform);
+
+			parentPlatformCatch.setParentPlatform(parentPlatform);
+
+			redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
+		}
+	}
+
+}

+ 57 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/utils/DateUtil.java

@@ -0,0 +1,57 @@
+package com.zd.alg.iot.vmp.gb28181.utils;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * @Description:时间工具类,主要处理ISO 8601格式转换
+ * @author: dgs
+ * @date:   2020年5月8日 下午3:24:42
+ */
+public class DateUtil {
+
+	//private static final String yyyy_MM_dd_T_HH_mm_ss_SSSXXX = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
+	private static final String yyyy_MM_dd_T_HH_mm_ss_SSSXXX = "yyyy-MM-dd'T'HH:mm:ss";
+    private static final String yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";
+
+	public static String yyyy_MM_dd_HH_mm_ssToISO8601(String formatTime) {
+
+        SimpleDateFormat oldsdf = new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss, Locale.getDefault());
+        SimpleDateFormat newsdf = new SimpleDateFormat(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault());
+        try {
+            return newsdf.format(oldsdf.parse(formatTime));
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+	public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) {
+
+        SimpleDateFormat oldsdf = new SimpleDateFormat(yyyy_MM_dd_T_HH_mm_ss_SSSXXX, Locale.getDefault());
+        SimpleDateFormat newsdf = new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss, Locale.getDefault());
+        try {
+            return newsdf.format(oldsdf.parse(formatTime));
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        return "";
+    }
+
+	public static long yyyy_MM_dd_HH_mm_ssToTimestamp(String formatTime) {
+		SimpleDateFormat format=new SimpleDateFormat(yyyy_MM_dd_HH_mm_ss);
+		//设置要读取的时间字符串格式
+		Date date;
+		try {
+			date = format.parse(formatTime);
+			Long timestamp=date.getTime()/1000;
+			//转换为Date类
+			return timestamp;
+		} catch (ParseException e) {
+			e.printStackTrace();
+		}
+		return 0;
+	}
+}

+ 46 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/utils/NumericUtil.java

@@ -0,0 +1,46 @@
+package com.zd.alg.iot.vmp.gb28181.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 数值格式判断和处理
+ * @author lawrencehj
+ * @date 2021年1月27日
+ */
+public class NumericUtil {
+
+    private static Logger logger = LoggerFactory.getLogger(NumericUtil.class);
+
+    /**
+     * 判断是否Double格式
+     * @param str
+     * @return true/false
+     */
+    public static boolean isDouble(String str) {
+        try {
+            Double num2 = Double.valueOf(str);
+//            logger.debug(num2 + " is a valid numeric string!");
+            return true;
+        } catch (Exception e) {
+//            logger.debug(str + " is an invalid numeric string!");
+            return false;
+        }
+    }
+
+    /**
+     * 判断是否Double格式
+     * @param str
+     * @return true/false
+     */
+    public static boolean isInteger(String str) {
+        try {
+            int num2 = Integer.valueOf(str);
+//            logger.debug(num2 + " is an integer!");
+            return true;
+        } catch (Exception e) {
+//            logger.debug(str + " is not an integer!");
+            return false;
+        }
+    }
+}

+ 164 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/gb28181/utils/XmlUtil.java

@@ -0,0 +1,164 @@
+package com.zd.alg.iot.vmp.gb28181.utils;
+
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+
+import org.dom4j.Attribute;
+import org.dom4j.Document;
+import org.dom4j.DocumentException;
+import org.dom4j.Element;
+import org.dom4j.io.SAXReader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+
+/**
+ * 基于dom4j的工具包
+ *
+ *
+ */
+public class XmlUtil {
+    /**
+     * 日志服务
+     */
+    private static Logger LOG = LoggerFactory.getLogger(XmlUtil.class);
+
+    /**
+     * 解析XML为Document对象
+     *
+     * @param xml 被解析的XMl
+     *
+     * @return Document
+     */
+    public static Element parseXml(String xml) {
+        Document document = null;
+        //
+        StringReader sr = new StringReader(xml);
+        SAXReader saxReader = new SAXReader();
+        try {
+            document = saxReader.read(sr);
+        } catch (DocumentException e) {
+            LOG.error("解析失败", e);
+        }
+        return null == document ? null : document.getRootElement();
+    }
+
+    /**
+     * 获取element对象的text的值
+     *
+     * @param em  节点的对象
+     * @param tag 节点的tag
+     * @return 节点
+     */
+    public static String getText(Element em, String tag) {
+        if (null == em) {
+            return null;
+        }
+        Element e = em.element(tag);
+        //
+        return null == e ? null : e.getText();
+    }
+
+    /**
+     * 递归解析xml节点,适用于 多节点数据
+     *
+     * @param node     node
+     * @param nodeName nodeName
+     * @return List<Map<String, Object>>
+     */
+    public static List<Map<String, Object>> listNodes(Element node, String nodeName) {
+        if (null == node) {
+            return null;
+        }
+        // 初始化返回
+        List<Map<String, Object>> listMap = new ArrayList<Map<String, Object>>();
+        // 首先获取当前节点的所有属性节点
+        List<Attribute> list = node.attributes();
+
+        Map<String, Object> map = null;
+        // 遍历属性节点
+        for (Attribute attribute : list) {
+            if (nodeName.equals(node.getName())) {
+                if (null == map) {
+                    map = new HashMap<String, Object>();
+                    listMap.add(map);
+                }
+                // 取到的节点属性放到map中
+                map.put(attribute.getName(), attribute.getValue());
+            }
+
+        }
+        // 遍历当前节点下的所有节点 ,nodeName 要解析的节点名称
+        // 使用递归
+        Iterator<Element> iterator = node.elementIterator();
+        while (iterator.hasNext()) {
+            Element e = iterator.next();
+            listMap.addAll(listNodes(e, nodeName));
+        }
+        return listMap;
+    }
+
+    /**
+     * xml转json
+     *
+     * @param element
+     * @param json
+     */
+    public static void node2Json(Element element, JSONObject json) {
+        // 如果是属性
+        for (Object o : element.attributes()) {
+            Attribute attr = (Attribute) o;
+            if (!StringUtils.isEmpty(attr.getValue())) {
+                json.put("@" + attr.getName(), attr.getValue());
+            }
+        }
+        List<Element> chdEl = element.elements();
+        if (chdEl.isEmpty() && !StringUtils.isEmpty(element.getText())) {// 如果没有子元素,只有一个值
+            json.put(element.getName(), element.getText());
+        }
+
+        for (Element e : chdEl) {   // 有子元素
+            if (!e.elements().isEmpty()) {  // 子元素也有子元素
+                JSONObject chdjson = new JSONObject();
+                node2Json(e, chdjson);
+                Object o = json.get(e.getName());
+                if (o != null) {
+                    JSONArray jsona = null;
+                    if (o instanceof JSONObject) {  // 如果此元素已存在,则转为jsonArray
+                        JSONObject jsono = (JSONObject) o;
+                        json.remove(e.getName());
+                        jsona = new JSONArray();
+                        jsona.add(jsono);
+                        jsona.add(chdjson);
+                    }
+                    if (o instanceof JSONArray) {
+                        jsona = (JSONArray) o;
+                        jsona.add(chdjson);
+                    }
+                    json.put(e.getName(), jsona);
+                } else {
+                    if (!chdjson.isEmpty()) {
+                        json.put(e.getName(), chdjson);
+                    }
+                }
+            } else { // 子元素没有子元素
+                for (Object o : element.attributes()) {
+                    Attribute attr = (Attribute) o;
+                    if (!StringUtils.isEmpty(attr.getValue())) {
+                        json.put("@" + attr.getName(), attr.getValue());
+                    }
+                }
+                if (!e.getText().isEmpty()) {
+                    json.put(e.getName(), e.getText());
+                }
+            }
+        }
+    }
+}

+ 436 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/ZLMHttpHookListener.java

@@ -0,0 +1,436 @@
+package com.zd.alg.iot.vmp.media.zlm;
+
+import java.util.List;
+import java.util.UUID;
+
+import com.alibaba.fastjson.JSONArray;
+import com.zd.alg.iot.vmp.common.StreamInfo;
+import com.zd.alg.iot.vmp.conf.MediaConfig;
+import com.zd.alg.iot.vmp.conf.UserSetup;
+import com.zd.alg.iot.vmp.gb28181.bean.Device;
+import com.zd.alg.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
+import com.zd.alg.iot.vmp.service.IMediaServerService;
+import com.zd.alg.iot.vmp.service.IPlayService;
+import com.zd.alg.iot.vmp.service.bean.SSRCInfo;
+import com.zd.alg.iot.vmp.storager.IRedisCatchStorage;
+import com.zd.alg.iot.vmp.storager.IVideoManagerStorager;
+import com.zd.alg.iot.vmp.media.zlm.dto.MediaServerItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+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 javax.servlet.http.HttpServletRequest;
+
+/**
+ * @Description:针对 ZLMediaServer的hook事件监听
+ * @author: dgs
+ * @date:   2020年5月8日 上午10:46:48
+ */
+@RestController
+@RequestMapping("/index/hook")
+public class ZLMHttpHookListener {
+
+	private final static Logger logger = LoggerFactory.getLogger(ZLMHttpHookListener.class);
+
+	@Autowired
+	private SIPCommander cmder;
+
+	@Autowired
+	private IPlayService playService;
+
+	@Autowired
+	private IVideoManagerStorager storager;
+
+	@Autowired
+	private IRedisCatchStorage redisCatchStorage;
+
+	@Autowired
+	private IMediaServerService mediaServerService;
+
+	@Autowired
+	private ZLMRESTfulUtils zlmresTfulUtils;
+
+	 @Autowired
+	 private ZLMMediaListManager zlmMediaListManager;
+
+	@Autowired
+	private ZLMHttpHookSubscribe subscribe;
+
+	@Autowired
+	private UserSetup userSetup;
+
+	@Autowired
+	private MediaConfig mediaConfig;
+
+	/**
+	 * 流量统计事件,播放器或推流器断开时并且耗用流量超过特定阈值时会触发此事件,阈值通过配置文件general.flowThreshold配置;此事件对回复不敏感。
+	 *
+	 */
+	@ResponseBody
+	@PostMapping(value = "/on_flow_report", produces = "application/json;charset=UTF-8")
+	public ResponseEntity<String> onFlowReport(@RequestBody JSONObject json){
+
+		if (logger.isDebugEnabled()) {
+			logger.debug("ZLM HOOK on_flow_report API调用,参数:" + json.toString());
+		}
+		String mediaServerId = json.getString("mediaServerId");
+		JSONObject ret = new JSONObject();
+		ret.put("code", 0);
+		ret.put("msg", "success");
+		return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
+	}
+
+	/**
+	 * 访问http文件服务器上hls之外的文件时触发。
+	 *
+	 */
+	@ResponseBody
+	@PostMapping(value = "/on_http_access", produces = "application/json;charset=UTF-8")
+	public ResponseEntity<String> 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 new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
+	}
+
+	/**
+	 * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。
+	 *
+	 */
+	@ResponseBody
+	@PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
+	public ResponseEntity<String> onPlay(@RequestBody JSONObject json){
+
+		if (logger.isDebugEnabled()) {
+			logger.debug("ZLM HOOK on_play API调用,参数:" + json.toString());
+		}
+		String mediaServerId = json.getString("mediaServerId");
+		ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_play, json);
+		if (subscribe != null ) {
+			MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+			if (mediaInfo != null) {
+				subscribe.response(mediaInfo, json);
+			}
+
+		}
+		JSONObject ret = new JSONObject();
+		ret.put("code", 0);
+		ret.put("msg", "success");
+		return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
+	}
+
+	/**
+	 * rtsp/rtmp/rtp推流鉴权事件。
+	 *
+	 */
+	@ResponseBody
+	@PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8")
+	public ResponseEntity<String> onPublish(@RequestBody JSONObject json) {
+
+		logger.debug("ZLM HOOK on_publish API调用,参数:" + json.toString());
+
+		String mediaServerId = json.getString("mediaServerId");
+		ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_publish, json);
+		if (subscribe != null) {
+			MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+			if (mediaInfo != null) {
+				subscribe.response(mediaInfo, json);
+			}
+		}
+		JSONObject ret = new JSONObject();
+		ret.put("code", 0);
+		ret.put("msg", "success");
+		ret.put("enableHls", true);
+		ret.put("enableMP4", userSetup.isRecordPushLive());
+		ret.put("enableRtxp", true);
+		return new ResponseEntity<String>(ret.toString(), HttpStatus.OK);
+	}
+
+	/**
+	 * 录制mp4完成后通知事件;此事件对回复不敏感。
+	 *
+	 */
+	@ResponseBody
+	@PostMapping(value = "/on_record_mp4", produces = "application/json;charset=UTF-8")
+	public ResponseEntity<String> 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 new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
+	}
+
+	/**
+	 * rtsp专用的鉴权事件,先触发on_rtsp_realm事件然后才会触发on_rtsp_auth事件。
+	 *
+	 */
+	@ResponseBody
+	@PostMapping(value = "/on_rtsp_realm", produces = "application/json;charset=UTF-8")
+	public ResponseEntity<String> 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 new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
+	}
+
+
+	/**
+	 * 该rtsp流是否开启rtsp专用方式的鉴权事件,开启后才会触发on_rtsp_auth事件。需要指出的是rtsp也支持url参数鉴权,它支持两种方式鉴权。
+	 *
+	 */
+	@ResponseBody
+	@PostMapping(value = "/on_rtsp_auth", produces = "application/json;charset=UTF-8")
+	public ResponseEntity<String> 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 new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
+	}
+
+	/**
+	 * shell登录鉴权,ZLMediaKit提供简单的telnet调试方式,使用telnet 127.0.0.1 9000能进入MediaServer进程的shell界面。
+	 *
+	 */
+	@ResponseBody
+	@PostMapping(value = "/on_shell_login", produces = "application/json;charset=UTF-8")
+	public ResponseEntity<String> onShellLogin(@RequestBody JSONObject json){
+
+		if (logger.isDebugEnabled()) {
+			logger.debug("ZLM HOOK on_shell_login API调用,参数:" + json.toString());
+		}
+		// TODO 如果是带有rtpstream则开启按需拉流
+		// String app = json.getString("app");
+		// String stream = json.getString("stream");
+		String mediaServerId = json.getString("mediaServerId");
+		ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_shell_login, json);
+		if (subscribe != null ) {
+			MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+			if (mediaInfo != null) {
+				subscribe.response(mediaInfo, json);
+			}
+
+		}
+
+		JSONObject ret = new JSONObject();
+		ret.put("code", 0);
+		ret.put("msg", "success");
+		return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
+	}
+
+	/**
+	 * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
+	 *
+	 */
+	@ResponseBody
+	@PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8")
+	public ResponseEntity<String> onStreamChanged(@RequestBody JSONObject json){
+
+		if (logger.isInfoEnabled()) {
+			logger.info("ZLM HOOK on_stream_changed API调用,参数:" + json.toString());
+		}
+		String mediaServerId = json.getString("mediaServerId");
+		ZLMHttpHookSubscribe.Event subscribe = this.subscribe.getSubscribe(ZLMHttpHookSubscribe.HookType.on_stream_changed, json);
+		if (subscribe != null ) {
+			MediaServerItem mediaInfo = mediaServerService.getOne(mediaServerId);
+			if (mediaInfo != null) {
+				subscribe.response(mediaInfo, json);
+			}
+
+		}
+
+		// 流消失移除redis play
+		String app = json.getString("app");
+		String streamId = json.getString("stream");
+		String schema = json.getString("schema");
+		JSONArray tracks = json.getJSONArray("tracks");
+		boolean regist = json.getBoolean("regist");
+		if (tracks != null) {
+			logger.info("[stream: " + streamId + "] on_stream_changed->>" + schema);
+		}
+		if ("rtmp".equals(schema)){
+			if (regist) {
+				mediaServerService.addCount(mediaServerId);
+			}else {
+				mediaServerService.removeCount(mediaServerId);
+			}
+			if ("rtp".equals(app) && !regist ) {
+				StreamInfo streamInfo = redisCatchStorage.queryPlayByStreamId(streamId);
+				if (streamInfo!=null){
+					redisCatchStorage.stopPlay(streamInfo);
+					storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId());
+				}else{
+					streamInfo = redisCatchStorage.queryPlaybackByStreamId(streamId);
+					redisCatchStorage.stopPlayback(streamInfo);
+				}
+			}else {
+				if (!"rtp".equals(app) ){
+					MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
+					if (regist) {
+						zlmMediaListManager.addMedia(mediaServerItem, app, streamId);
+					}else {
+						zlmMediaListManager.removeMedia( app, streamId);
+					}
+				}
+			}
+		}
+
+		JSONObject ret = new JSONObject();
+		ret.put("code", 0);
+		ret.put("msg", "success");
+		return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
+	}
+
+	/**
+	 * 流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流。
+	 *
+	 */
+	@ResponseBody
+	@PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8")
+	public ResponseEntity<String> onStreamNoneReader(@RequestBody JSONObject json){
+
+		if (logger.isDebugEnabled()) {
+			logger.debug("ZLM HOOK on_stream_none_reader API调用,参数:" + json.toString());
+		}
+		String mediaServerId = json.getString("mediaServerId");
+		String streamId = json.getString("stream");
+		String app = json.getString("app");
+
+//		if ("rtp".equals(app)){
+//			JSONObject ret = new JSONObject();
+//			ret.put("code", 0);
+//			ret.put("close", true);
+//			StreamInfo streamInfoForPlayCatch = redisCatchStorage.queryPlayByStreamId(streamId);
+//			if (streamInfoForPlayCatch != null) {
+//				if (redisCatchStorage.isChannelSendingRTP(streamInfoForPlayCatch.getChannelId())) {
+//					ret.put("close", false);
+//				} else {
+//					cmder.streamByeCmd(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
+//					redisCatchStorage.stopPlay(streamInfoForPlayCatch);
+//					storager.stopPlay(streamInfoForPlayCatch.getDeviceID(), streamInfoForPlayCatch.getChannelId());
+//				}
+//			}else{
+//				StreamInfo streamInfoForPlayBackCatch = redisCatchStorage.queryPlaybackByStreamId(streamId);
+//				if (streamInfoForPlayBackCatch != null) {
+//					cmder.streamByeCmd(streamInfoForPlayBackCatch.getDeviceID(), streamInfoForPlayBackCatch.getChannelId());
+//					redisCatchStorage.stopPlayback(streamInfoForPlayBackCatch);
+//				}
+//			}
+//			MediaServerItem mediaServerItem = mediaServerService.getOne(mediaServerId);
+//			if (mediaServerItem != null && "-1".equals(mediaServerItem.getStreamNoneReaderDelayMS())) {
+//				ret.put("close", false);
+//			}
+//			return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
+//		}else {
+//			JSONObject ret = new JSONObject();
+//			ret.put("code", 0);
+//			ret.put("close", false);
+//			return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
+//		}
+		JSONObject ret = new JSONObject();
+		ret.put("code", 0);
+		ret.put("close", false);
+		return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
+	}
+
+	/**
+	 * 流未找到事件,用户可以在此事件触发时,立即去拉流,这样可以实现按需拉流;此事件对回复不敏感。
+	 *
+	 */
+	@ResponseBody
+	@PostMapping(value = "/on_stream_not_found", produces = "application/json;charset=UTF-8")
+	public ResponseEntity<String> 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 (userSetup.isAutoApplyPlay() && mediaInfo != null) {
+			String app = json.getString("app");
+			String streamId = json.getString("stream");
+			if ("rtp".equals(app)) {
+				String[] s = streamId.split("_");
+				if (s.length == 2) {
+					String deviceId = s[0];
+					String channelId = s[1];
+					Device device = storager.queryVideoDevice(deviceId);
+					if (device != null) {
+						UUID uuid = UUID.randomUUID();
+						SSRCInfo ssrcInfo;
+						String streamId2 = null;
+						if (mediaInfo.isRtpEnable()) {
+							streamId2 = String.format("%s_%s", device.getDeviceId(), channelId);
+						}
+						ssrcInfo = mediaServerService.openRTPServer(mediaInfo, streamId2);
+						cmder.playStreamCmd(mediaInfo, ssrcInfo, device, channelId, (MediaServerItem mediaServerItemInuse, JSONObject response) -> {
+							logger.info("收到订阅消息: " + response.toJSONString());
+							playService.onPublishHandlerForPlay(mediaServerItemInuse, response, deviceId, channelId, uuid.toString());
+						}, null);
+					}
+
+				}
+			}
+
+		}
+
+		JSONObject ret = new JSONObject();
+		ret.put("code", 0);
+		ret.put("msg", "success");
+		return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
+	}
+
+	/**
+	 * 服务器启动事件,可以用于监听服务器崩溃重启;此事件对回复不敏感。
+	 *
+	 */
+	@ResponseBody
+	@PostMapping(value = "/on_server_started", produces = "application/json;charset=UTF-8")
+	public ResponseEntity<String> 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(ZLMHttpHookSubscribe.HookType.on_server_started);
+		if (subscribes != null  && subscribes.size() > 0) {
+			for (ZLMHttpHookSubscribe.Event subscribe : subscribes) {
+				subscribe.response(null, jsonObject);
+			}
+		}
+		JSONObject ret = new JSONObject();
+		ret.put("code", 0);
+		ret.put("msg", "success");
+		return new ResponseEntity<String>(ret.toString(),HttpStatus.OK);
+	}
+}

+ 117 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/ZLMHttpHookSubscribe.java

@@ -0,0 +1,117 @@
+package com.zd.alg.iot.vmp.media.zlm;
+
+import com.alibaba.fastjson.JSONObject;
+import com.zd.alg.iot.vmp.media.zlm.dto.MediaServerItem;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @Description:针对 ZLMediaServer的hook事件订阅
+ * @author: pan
+ * @date:   2020年12月2日 21:17:32
+ */
+@Component
+public class ZLMHttpHookSubscribe {
+
+    public enum HookType{
+        on_flow_report,
+        on_http_access,
+        on_play,
+        on_publish,
+        on_record_mp4,
+        on_rtsp_auth,
+        on_rtsp_realm,
+        on_shell_login,
+        on_stream_changed,
+        on_stream_none_reader,
+        on_stream_not_found,
+        on_server_started
+    }
+
+    public interface Event{
+        void response(MediaServerItem mediaServerItem, JSONObject response);
+    }
+
+    private Map<HookType, Map<JSONObject, Event>> allSubscribes = new ConcurrentHashMap<>();
+
+    public void addSubscribe(HookType type, JSONObject hookResponse, Event event) {
+        Map<JSONObject, Event> eventMap = allSubscribes.get(type);
+        if (eventMap == null) {
+            eventMap = new HashMap<JSONObject, Event>();
+            allSubscribes.put(type,eventMap);
+        }
+        eventMap.put(hookResponse, event);
+    }
+
+    public Event getSubscribe(HookType type, JSONObject hookResponse) {
+        Event event= null;
+        Map<JSONObject, Event> eventMap = allSubscribes.get(type);
+        if (eventMap == null) {
+            return null;
+        }
+        for (JSONObject key : eventMap.keySet()) {
+            Boolean result = null;
+            for (String s : key.keySet()) {
+                if (result == null) {
+                    result = key.getString(s).equals(hookResponse.getString(s));
+                }else {
+                    if (key.getString(s) == null) {
+                        continue;
+                    }
+                    result = result && key.getString(s).equals(hookResponse.getString(s));
+                }
+
+            }
+            if (null != result && result) {
+                event = eventMap.get(key);
+            }
+        }
+        return event;
+    }
+
+    public void removeSubscribe(HookType type, JSONObject hookResponse) {
+        Map<JSONObject, Event> eventMap = allSubscribes.get(type);
+        if (eventMap == null) {
+            return;
+        }
+        Iterator<Map.Entry<JSONObject, Event>> iterator = eventMap.entrySet().iterator();
+        while (iterator.hasNext()){
+            Map.Entry<JSONObject, Event> next = iterator.next();
+            JSONObject key = next.getKey();
+            Boolean result = null;
+            for (String s : key.keySet()) {
+                if (result == null) {
+                    result = key.getString(s).equals(hookResponse.getString(s));
+                }else {
+                    if (key.getString(s) == null) continue;
+                    result = result && key.getString(s).equals(hookResponse.getString(s));
+                }
+            }
+            if (null != result && result){
+                iterator.remove();
+            }
+        }
+    }
+
+    /**
+     * 获取某个类型的所有的订阅
+     * @param type
+     * @return
+     */
+    public List<Event> getSubscribes(HookType type) {
+        // ZLMHttpHookSubscribe.Event event= null;
+        Map<JSONObject, Event> eventMap = allSubscribes.get(type);
+        if (eventMap == null) {
+            return null;
+        }
+        List<Event> result = new ArrayList<>();
+        for (JSONObject key : eventMap.keySet()) {
+            result.add(eventMap.get(key));
+        }
+        return result;
+    }
+
+
+}

+ 154 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/ZLMMediaListManager.java

@@ -0,0 +1,154 @@
+package com.zd.alg.iot.vmp.media.zlm;
+
+import com.alibaba.fastjson.JSONObject;
+import com.zd.alg.iot.vmp.service.IStreamPushService;
+import com.zd.alg.iot.vmp.storager.IRedisCatchStorage;
+import com.zd.alg.iot.vmp.storager.IVideoManagerStorager;
+import com.zd.alg.iot.vmp.storager.dao.GbStreamMapper;
+import com.zd.alg.iot.vmp.storager.dao.PlatformGbStreamMapper;
+import com.zd.alg.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.zd.alg.iot.vmp.media.zlm.dto.StreamProxyItem;
+import com.zd.alg.iot.vmp.media.zlm.dto.StreamPushItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+@Component
+public class ZLMMediaListManager {
+
+    private Logger logger = LoggerFactory.getLogger("ZLMMediaListManager");
+
+    @Autowired
+    private ZLMRESTfulUtils zlmresTfulUtils;
+
+    @Autowired
+    private IRedisCatchStorage redisCatchStorage;
+
+    @Autowired
+    private IVideoManagerStorager storager;
+
+    @Autowired
+    private GbStreamMapper gbStreamMapper;
+
+    @Autowired
+    private PlatformGbStreamMapper platformGbStreamMapper;
+
+    @Autowired
+    private IStreamPushService streamPushService;
+
+    @Autowired
+    private ZLMHttpHookSubscribe subscribe;
+
+
+    public void updateMediaList(MediaServerItem mediaServerItem) {
+        storager.clearMediaList();
+
+        // 使用异步的当时更新媒体流列表
+        zlmresTfulUtils.getMediaList(mediaServerItem, (mediaList ->{
+            if (mediaList == null) return;
+            String dataStr = mediaList.getString("data");
+
+            Integer code = mediaList.getInteger("code");
+            Map<String, StreamPushItem> result = new HashMap<>();
+            List<StreamPushItem> streamPushItems = null;
+            // 获取所有的国标关联
+//            List<GbStream> gbStreams = gbStreamMapper.selectAllByMediaServerId(mediaServerItem.getId());
+            if (code == 0 ) {
+                if (dataStr != null) {
+                    streamPushItems = streamPushService.handleJSON(dataStr, mediaServerItem);
+                }
+            }else {
+                logger.warn("更新视频流失败,错误code: " + code);
+            }
+
+            if (streamPushItems != null) {
+                storager.updateMediaList(streamPushItems);
+                for (StreamPushItem streamPushItem : streamPushItems) {
+                    JSONObject jsonObject = new JSONObject();
+                    jsonObject.put("app", streamPushItem.getApp());
+                    jsonObject.put("stream", streamPushItem.getStream());
+                    jsonObject.put("mediaServerId", mediaServerItem.getId());
+                    subscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_play,jsonObject,
+                            (MediaServerItem mediaServerItemInuse, JSONObject response)->{
+                                updateMedia(mediaServerItem, response.getString("app"), response.getString("stream"));
+                            }
+                    );
+                }
+            }
+        }));
+
+    }
+
+    public void addMedia(MediaServerItem mediaServerItem, String app, String streamId) {
+        //使用异步更新推流
+        updateMedia(mediaServerItem, app, streamId);
+    }
+
+
+    public void updateMedia(MediaServerItem mediaServerItem, String app, String streamId) {
+        //使用异步更新推流
+        zlmresTfulUtils.getMediaList(mediaServerItem, app, streamId, "rtmp", json->{
+
+            if (json == null) return;
+            String dataStr = json.getString("data");
+
+            Integer code = json.getInteger("code");
+            Map<String, StreamPushItem> result = new HashMap<>();
+            List<StreamPushItem> streamPushItems = null;
+            if (code == 0 ) {
+                if (dataStr != null) {
+                    streamPushItems = streamPushService.handleJSON(dataStr, mediaServerItem);
+                }
+            }else {
+                logger.warn("更新视频流失败,错误code: " + code);
+            }
+
+            if (streamPushItems != null && streamPushItems.size() == 1) {
+                storager.updateMedia(streamPushItems.get(0));
+            }
+        });
+    }
+
+
+    public void removeMedia(String app, String streamId) {
+        // 查找是否关联了国标, 关联了不删除, 置为离线
+        StreamProxyItem streamProxyItem = gbStreamMapper.selectOne(app, streamId);
+        if (streamProxyItem == null) {
+            storager.removeMedia(app, streamId);
+        }else {
+            storager.mediaOutline(app, streamId);
+        }
+    }
+
+//    public void clearAllSessions() {
+//        logger.info("清空所有国标相关的session");
+//        JSONObject allSessionJSON = zlmresTfulUtils.getAllSession();
+//        ZLMServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
+//        HashSet<String> allLocalPorts = new HashSet();
+//        if (allSessionJSON.getInteger("code") == 0) {
+//            JSONArray data = allSessionJSON.getJSONArray("data");
+//            if (data.size() > 0) {
+//                for (int i = 0; i < data.size(); i++) {
+//                    JSONObject sessionJOSN = data.getJSONObject(i);
+//                    Integer local_port = sessionJOSN.getInteger("local_port");
+//                    if (!local_port.equals(Integer.valueOf(mediaInfo.getHttpPort())) &&
+//                        !local_port.equals(Integer.valueOf(mediaInfo.getHttpSSLport())) &&
+//                        !local_port.equals(Integer.valueOf(mediaInfo.getRtmpPort())) &&
+//                        !local_port.equals(Integer.valueOf(mediaInfo.getRtspPort())) &&
+//                        !local_port.equals(Integer.valueOf(mediaInfo.getRtspSSlport())) &&
+//                        !local_port.equals(Integer.valueOf(mediaInfo.getHookOnFlowReport()))){
+//                        allLocalPorts.add(sessionJOSN.getInteger("local_port") + "");
+//                     }
+//                }
+//            }
+//        }
+//        if (allLocalPorts.size() > 0) {
+//            List<String> result = new ArrayList<>(allLocalPorts);
+//            String localPortSStr = String.join(",", result);
+//            zlmresTfulUtils.kickSessions(localPortSStr);
+//        }
+//    }
+}

+ 254 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/ZLMRESTfulUtils.java

@@ -0,0 +1,254 @@
+package com.zd.alg.iot.vmp.media.zlm;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.zd.alg.iot.vmp.media.zlm.dto.MediaServerItem;
+import okhttp3.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import javax.validation.constraints.NotNull;
+import java.io.*;
+import java.net.ConnectException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+@Component
+public class ZLMRESTfulUtils {
+
+    private final static Logger logger = LoggerFactory.getLogger(ZLMRESTfulUtils.class);
+
+    public interface RequestCallback{
+        void run(JSONObject response);
+    }
+
+    public JSONObject sendPost(MediaServerItem mediaServerItem, String api, Map<String, Object> param, RequestCallback callback) {
+        OkHttpClient client = new OkHttpClient();
+        String url = String.format("http://%s:%s/index/api/%s",  mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
+        JSONObject responseJSON = null;
+        logger.debug(url);
+
+        FormBody.Builder builder = new FormBody.Builder();
+        builder.add("secret",mediaServerItem.getSecret());
+        if (param != null && param.keySet().size() > 0) {
+            for (String key : param.keySet()){
+                if (param.get(key) != null) {
+                    builder.add(key, param.get(key).toString());
+                }
+            }
+        }
+
+        FormBody body = builder.build();
+
+        Request request = new Request.Builder()
+                .post(body)
+                .url(url)
+                .build();
+            if (callback == null) {
+                try {
+                    Response response = client.newCall(request).execute();
+                    if (response.isSuccessful()) {
+                        String responseStr = response.body().string();
+                        if (responseStr != null) {
+                            responseJSON = JSON.parseObject(responseStr);
+                        }
+                    }
+                } catch (ConnectException e) {
+                    logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage()));
+                    logger.info("请检查media配置并确认ZLM已启动...");
+                }catch (IOException e) {
+                    logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage()));
+                }
+            }else {
+                client.newCall(request).enqueue(new Callback(){
+
+                    @Override
+                    public void onResponse(@NotNull Call call, @NotNull Response response){
+                        if (response.isSuccessful()) {
+                            try {
+                                String responseStr = Objects.requireNonNull(response.body()).string();
+                                callback.run(JSON.parseObject(responseStr));
+                            } catch (IOException e) {
+                                logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage()));
+                            }
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(@NotNull Call call, @NotNull IOException e) {
+                        logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage()));
+                        logger.info("请检查media配置并确认ZLM已启动...");
+                    }
+                });
+            }
+
+
+
+        return responseJSON;
+    }
+
+
+    public void sendPostForImg(MediaServerItem mediaServerItem, String api, Map<String, Object> param, String targetPath, String fileName) {
+        OkHttpClient client = new OkHttpClient();
+        String url = String.format("http://%s:%s/index/api/%s",  mediaServerItem.getIp(), mediaServerItem.getHttpPort(), api);
+        JSONObject responseJSON = null;
+        logger.debug(url);
+        FormBody.Builder builder = new FormBody.Builder();
+        builder.add("secret",mediaServerItem.getSecret());
+        if (param != null && param.keySet().size() > 0) {
+            for (String key : param.keySet()){
+                if (param.get(key) != null) {
+                    builder.add(key, param.get(key).toString());
+                }
+            }
+        }
+        FormBody body = builder.build();
+        Request request = new Request.Builder()
+                .post(body)
+                .url(url)
+                .build();
+        try {
+            Response response = client.newCall(request).execute();
+            if (response.isSuccessful()) {
+                if (targetPath != null) {
+                    File snapFolder = new File(targetPath);
+                    if (!snapFolder.exists()) {
+                        snapFolder.mkdirs();
+                    }
+                    File snapFile = new File(targetPath + "/" + fileName);
+                    FileOutputStream outStream = new FileOutputStream(snapFile);
+                    outStream.write(response.body().bytes());
+                    outStream.close();
+                }
+            }
+        } catch (ConnectException e) {
+            logger.error(String.format("连接ZLM失败: %s, %s", e.getCause().getMessage(), e.getMessage()));
+            logger.info("请检查media配置并确认ZLM已启动...");
+        }catch (IOException e) {
+            logger.error(String.format("[ %s ]请求失败: %s", url, e.getMessage()));
+        }
+
+    }
+
+
+    public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream, String schema, RequestCallback callback){
+        Map<String, Object> param = new HashMap<>();
+        if (app != null) param.put("app",app);
+        if (stream != null) param.put("stream",stream);
+        if (schema != null) param.put("schema",schema);
+        param.put("vhost","__defaultVhost__");
+        return sendPost(mediaServerItem, "getMediaList",param, callback);
+    }
+
+    public JSONObject getMediaList(MediaServerItem mediaServerItem, String app, String stream){
+        return getMediaList(mediaServerItem, app, stream,null,  null);
+    }
+
+    public JSONObject getMediaList(MediaServerItem mediaServerItem, RequestCallback callback){
+        return sendPost(mediaServerItem, "getMediaList",null, callback);
+    }
+
+    public JSONObject getMediaInfo(MediaServerItem mediaServerItem, String app, String schema, String stream){
+        Map<String, Object> param = new HashMap<>();
+        param.put("app",app);
+        param.put("schema",schema);
+        param.put("stream",stream);
+        param.put("vhost","__defaultVhost__");
+        return sendPost(mediaServerItem, "getMediaInfo",param, null);
+    }
+
+    public JSONObject getRtpInfo(MediaServerItem mediaServerItem, String stream_id){
+        Map<String, Object> param = new HashMap<>();
+        param.put("stream_id",stream_id);
+        return sendPost(mediaServerItem, "getRtpInfo",param, null);
+    }
+
+    public JSONObject addFFmpegSource(MediaServerItem mediaServerItem, String src_url, String dst_url, String timeout_ms,
+                                      boolean enable_hls, boolean enable_mp4, String ffmpeg_cmd_key){
+        logger.info(src_url);
+        logger.info(dst_url);
+        Map<String, Object> param = new HashMap<>();
+        param.put("src_url", src_url);
+        param.put("dst_url", dst_url);
+        param.put("timeout_ms", timeout_ms);
+        param.put("enable_hls", enable_hls);
+        param.put("enable_mp4", enable_mp4);
+        param.put("ffmpeg_cmd_key", ffmpeg_cmd_key);
+        return sendPost(mediaServerItem, "addFFmpegSource",param, null);
+    }
+
+    public JSONObject delFFmpegSource(MediaServerItem mediaServerItem, String key){
+        Map<String, Object> param = new HashMap<>();
+        param.put("key", key);
+        return sendPost(mediaServerItem, "delFFmpegSource",param, null);
+    }
+
+    public JSONObject getMediaServerConfig(MediaServerItem mediaServerItem){
+        return sendPost(mediaServerItem, "getServerConfig",null, null);
+    }
+
+    public JSONObject setServerConfig(MediaServerItem mediaServerItem, Map<String, Object> param){
+        return sendPost(mediaServerItem,"setServerConfig",param, null);
+    }
+
+    public JSONObject openRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param){
+        return sendPost(mediaServerItem, "openRtpServer",param, null);
+    }
+
+    public JSONObject closeRtpServer(MediaServerItem mediaServerItem, Map<String, Object> param) {
+        return sendPost(mediaServerItem, "closeRtpServer",param, null);
+    }
+
+    public JSONObject listRtpServer(MediaServerItem mediaServerItem) {
+        return sendPost(mediaServerItem, "listRtpServer",null, null);
+    }
+
+    public JSONObject startSendRtp(MediaServerItem mediaServerItem, Map<String, Object> param) {
+        return sendPost(mediaServerItem, "startSendRtp",param, null);
+    }
+
+    public JSONObject stopSendRtp(MediaServerItem mediaServerItem, Map<String, Object> param) {
+        return sendPost(mediaServerItem, "stopSendRtp",param, null);
+    }
+
+    public JSONObject addStreamProxy(MediaServerItem mediaServerItem, String app, String stream, String url, boolean enable_hls, boolean enable_mp4, String rtp_type) {
+        Map<String, Object> param = new HashMap<>();
+        param.put("vhost", "__defaultVhost__");
+        param.put("app", app);
+        param.put("stream", stream);
+        param.put("url", url);
+        param.put("enable_hls", enable_hls?1:0);
+        param.put("enable_mp4", enable_mp4?1:0);
+        param.put("rtp_type", rtp_type);
+        return sendPost(mediaServerItem, "addStreamProxy",param, null);
+    }
+
+    public JSONObject closeStreams(MediaServerItem mediaServerItem, String app, String stream) {
+        Map<String, Object> param = new HashMap<>();
+        param.put("vhost", "__defaultVhost__");
+        param.put("app", app);
+        param.put("stream", stream);
+        param.put("force", 1);
+        return sendPost(mediaServerItem, "close_streams",param, null);
+    }
+
+    public JSONObject getAllSession(MediaServerItem mediaServerItem) {
+        return sendPost(mediaServerItem, "getAllSession",null, null);
+    }
+
+    public void kickSessions(MediaServerItem mediaServerItem, String localPortSStr) {
+        Map<String, Object> param = new HashMap<>();
+        param.put("local_port", localPortSStr);
+        sendPost(mediaServerItem, "kick_sessions",param, null);
+    }
+
+    public void getSnap(MediaServerItem mediaServerItem, String flvUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
+        Map<String, Object> param = new HashMap<>();
+        param.put("url", flvUrl);
+        param.put("timeout_sec", timeout_sec);
+        param.put("expire_sec", expire_sec);
+        sendPostForImg(mediaServerItem, "getSnap",param, targetPath, fileName);
+    }
+}

+ 256 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/ZLMRTPServerFactory.java

@@ -0,0 +1,256 @@
+package com.zd.alg.iot.vmp.media.zlm;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.zd.alg.iot.vmp.gb28181.bean.SendRtpItem;
+import com.zd.alg.iot.vmp.media.zlm.dto.MediaServerItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+public class ZLMRTPServerFactory {
+
+    private Logger logger = LoggerFactory.getLogger("ZLMRTPServerFactory");
+
+    @Autowired
+    private ZLMRESTfulUtils zlmresTfulUtils;
+
+    private int[] portRangeArray = new int[2];
+
+    public int createRTPServer(MediaServerItem mediaServerItem, String streamId) {
+        Map<String, Integer> currentStreams = new HashMap<>();
+        JSONObject listRtpServerJsonResult = zlmresTfulUtils.listRtpServer(mediaServerItem);
+        if (listRtpServerJsonResult != null) {
+            JSONArray data = listRtpServerJsonResult.getJSONArray("data");
+            if (data != null) {
+                for (int i = 0; i < data.size(); i++) {
+                    JSONObject dataItem = data.getJSONObject(i);
+                    currentStreams.put(dataItem.getString("stream_id"), dataItem.getInteger("port"));
+                }
+            }
+        }
+        // 已经在推流
+        if (currentStreams.get(streamId) != null) {
+            Map<String, Object> closeRtpServerParam = new HashMap<>();
+            closeRtpServerParam.put("stream_id", streamId);
+            zlmresTfulUtils.closeRtpServer(mediaServerItem, closeRtpServerParam);
+            currentStreams.remove(streamId);
+        }
+
+        Map<String, Object> param = new HashMap<>();
+        int result = -1;
+        int newPort = getPortFromportRange(mediaServerItem);
+        param.put("port", newPort);
+        param.put("enable_tcp", 1);
+        param.put("stream_id", streamId);
+        JSONObject openRtpServerResultJson = zlmresTfulUtils.openRtpServer(mediaServerItem, param);
+
+        if (openRtpServerResultJson != null) {
+            switch (openRtpServerResultJson.getInteger("code")){
+                case 0:
+                    result= newPort;
+                    break;
+                case -300: // id已经存在, 可能已经在其他端口推流
+                    Map<String, Object> closeRtpServerParam = new HashMap<>();
+                    closeRtpServerParam.put("stream_id", streamId);
+                    zlmresTfulUtils.closeRtpServer(mediaServerItem, closeRtpServerParam);
+                    result = newPort;
+                    break;
+                case -400: // 端口占用
+                    result= createRTPServer(mediaServerItem, streamId);
+                    break;
+                default:
+                    logger.error("创建RTP Server 失败 {}: " + openRtpServerResultJson.getString("msg"), newPort);
+                    break;
+            }
+        }else {
+            //  检查ZLM状态
+            logger.error("创建RTP Server 失败 {}: 请检查ZLM服务", newPort);
+        }
+        return result;
+    }
+
+    public boolean closeRTPServer(MediaServerItem serverItem, String streamId) {
+        boolean result = false;
+        if (serverItem !=null){
+            Map<String, Object> param = new HashMap<>();
+            param.put("stream_id", streamId);
+            JSONObject jsonObject = zlmresTfulUtils.closeRtpServer(serverItem, param);
+            if (jsonObject != null ) {
+                if (jsonObject.getInteger("code") == 0) {
+                    result = jsonObject.getInteger("hit") == 1;
+                }else {
+                    logger.error("关闭RTP Server 失败: " + jsonObject.getString("msg"));
+                }
+            }else {
+                //  检查ZLM状态
+                logger.error("关闭RTP Server 失败: 请检查ZLM服务");
+            }
+        }
+        return result;
+    }
+
+    private int getPortFromportRange(MediaServerItem mediaServerItem) {
+        int currentPort = mediaServerItem.getCurrentPort();
+        if (currentPort == 0) {
+            String[] portRangeStrArray = mediaServerItem.getRtpPortRange().split(",");
+            portRangeArray[0] = Integer.parseInt(portRangeStrArray[0]);
+            portRangeArray[1] = Integer.parseInt(portRangeStrArray[1]);
+        }
+
+        if (currentPort == 0 || currentPort++ > portRangeArray[1]) {
+            currentPort = portRangeArray[0];
+            mediaServerItem.setCurrentPort(currentPort);
+            return portRangeArray[0];
+        } else {
+            if (currentPort % 2 == 1) {
+                currentPort++;
+            }
+            currentPort++;
+            mediaServerItem.setCurrentPort(currentPort);
+            return currentPort;
+        }
+    }
+
+    /**
+     * 创建一个国标推流
+     * @param ip 推流ip
+     * @param port 推流端口
+     * @param ssrc 推流唯一标识
+     * @param platformId 平台id
+     * @param channelId 通道id
+     * @param tcp 是否为tcp
+     * @return SendRtpItem
+     */
+    public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String deviceId, String channelId, boolean tcp){
+
+        // 使用RTPServer 功能找一个可用的端口
+        String playSsrc = serverItem.getSsrcConfig().getPlaySsrc();
+        int localPort = createRTPServer(serverItem, playSsrc);
+        if (localPort != -1) {
+            // TODO 高并发时可能因为未放入缓存而ssrc冲突
+            serverItem.getSsrcConfig().releaseSsrc(playSsrc);
+            closeRTPServer(serverItem, playSsrc);
+        }else {
+            logger.error("没有可用的端口");
+            return null;
+        }
+        SendRtpItem sendRtpItem = new SendRtpItem();
+        sendRtpItem.setIp(ip);
+        sendRtpItem.setPort(port);
+        sendRtpItem.setSsrc(ssrc);
+        sendRtpItem.setPlatformId(platformId);
+        sendRtpItem.setDeviceId(deviceId);
+        sendRtpItem.setChannelId(channelId);
+        sendRtpItem.setTcp(tcp);
+        sendRtpItem.setApp("rtp");
+        sendRtpItem.setLocalPort(localPort);
+        sendRtpItem.setMediaServerId(serverItem.getId());
+        return sendRtpItem;
+    }
+
+    /**
+     * 创建一个直播推流
+     * @param ip 推流ip
+     * @param port 推流端口
+     * @param ssrc 推流唯一标识
+     * @param platformId 平台id
+     * @param channelId 通道id
+     * @param tcp 是否为tcp
+     * @return SendRtpItem
+     */
+    public SendRtpItem createSendRtpItem(MediaServerItem serverItem, String ip, int port, String ssrc, String platformId, String app, String stream, String channelId, boolean tcp){
+        String playSsrc = serverItem.getSsrcConfig().getPlaySsrc();
+        int localPort = createRTPServer(serverItem, playSsrc);
+        if (localPort != -1) {
+            // TODO 高并发时可能因为未放入缓存而ssrc冲突
+            serverItem.getSsrcConfig().releaseSsrc(ssrc);
+            closeRTPServer(serverItem, playSsrc);
+        }else {
+            logger.error("没有可用的端口");
+            return null;
+        }
+        SendRtpItem sendRtpItem = new SendRtpItem();
+        sendRtpItem.setIp(ip);
+        sendRtpItem.setPort(port);
+        sendRtpItem.setSsrc(ssrc);
+        sendRtpItem.setApp(app);
+        sendRtpItem.setStreamId(stream);
+        sendRtpItem.setPlatformId(platformId);
+        sendRtpItem.setChannelId(channelId);
+        sendRtpItem.setTcp(tcp);
+        sendRtpItem.setLocalPort(localPort);
+        sendRtpItem.setMediaServerId(serverItem.getId());
+        return sendRtpItem;
+    }
+
+    /**
+     * 调用zlm RESTful API —— startSendRtp
+     */
+    public Boolean startSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) {
+        Boolean result = false;
+        JSONObject jsonObject = zlmresTfulUtils.startSendRtp(mediaServerItem, param);
+        if (jsonObject == null) {
+            logger.error("RTP推流失败: 请检查ZLM服务");
+        } else if (jsonObject.getInteger("code") == 0) {
+            result= true;
+            logger.info("RTP推流[ {}/{} ]请求成功,本地推流端口:{}" ,param.get("app"), param.get("stream"), jsonObject.getString("local_port"));
+        } else {
+            logger.error("RTP推流失败: " + jsonObject.getString("msg"));
+        }
+        return result;
+    }
+
+    /**
+     * 查询待转推的流是否就绪
+     */
+    public Boolean isRtpReady(MediaServerItem mediaServerItem, String streamId) {
+        JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem,"rtp", "rtmp", streamId);
+        return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online"));
+    }
+
+    /**
+     * 查询待转推的流是否就绪
+     */
+    public Boolean isStreamReady(MediaServerItem mediaServerItem, String app, String streamId) {
+        JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId);
+        return (mediaInfo.getInteger("code") == 0 && mediaInfo.getBoolean("online"));
+    }
+
+    /**
+     * 查询转推的流是否有其它观看者
+     * @param streamId
+     * @return
+     */
+    public int totalReaderCount(MediaServerItem mediaServerItem, String app, String streamId) {
+        JSONObject mediaInfo = zlmresTfulUtils.getMediaInfo(mediaServerItem, app, "rtmp", streamId);
+        if (mediaInfo == null) return 0;
+        return mediaInfo.getInteger("totalReaderCount");
+    }
+
+    /**
+     * 调用zlm RESTful API —— stopSendRtp
+     */
+    public Boolean stopSendRtpStream(MediaServerItem mediaServerItem, Map<String, Object>param) {
+        Boolean result = false;
+        JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaServerItem, param);
+        if (jsonObject == null) {
+            logger.error("停止RTP推流失败: 请检查ZLM服务");
+        } else if (jsonObject.getInteger("code") == 0) {
+            result= true;
+            logger.info("停止RTP推流成功");
+        } else {
+            logger.error("停止RTP推流失败: " + jsonObject.getString("msg"));
+        }
+        return result;
+    }
+
+    public void closeAllSendRtpStream() {
+
+    }
+}

+ 166 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/ZLMRunner.java

@@ -0,0 +1,166 @@
+package com.zd.alg.iot.vmp.media.zlm;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.zd.alg.iot.vmp.conf.MediaConfig;
+import com.zd.alg.iot.vmp.media.zlm.dto.MediaServerItem;
+import com.zd.alg.iot.vmp.service.IMediaServerService;
+import com.zd.alg.iot.vmp.service.IStreamProxyService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+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.*;
+
+@Component
+@Order(value=1)
+public class ZLMRunner implements CommandLineRunner {
+
+    private final static Logger logger = LoggerFactory.getLogger(ZLMRunner.class);
+
+    private Map<String, Boolean> startGetMedia;
+
+    @Autowired
+    private ZLMRESTfulUtils zlmresTfulUtils;
+
+    @Autowired
+    private ZLMHttpHookSubscribe hookSubscribe;
+
+    @Autowired
+    private IStreamProxyService streamProxyService;
+
+    @Autowired
+    private IMediaServerService mediaServerService;
+
+    @Autowired
+    private MediaConfig mediaConfig;
+
+    @Override
+    public void run(String... strings) throws Exception {
+        // 清楚redis缓存的在线zlm信息
+        mediaServerService.clearMediaServerForOnline();
+
+        // 将配置文件的meida配置写入数据库
+//        MediaServerItem presetMediaServer = mediaServerService.getOneByHostAndPort(
+//                mediaConfig.getIp(), mediaConfig.getHttpPort());
+//        if (presetMediaServer  != null) {
+//            MediaServerItem mediaSerItem = mediaConfig.getMediaSerItem();
+//            mediaSerItem.setId(presetMediaServer.getId());
+//            mediaServerService.update(mediaSerItem);
+//        }else {
+//            if (mediaConfig.getId() != null) {
+//                MediaServerItem mediaSerItem = mediaConfig.getMediaSerItem();
+//                mediaServerService.add(mediaSerItem);
+//            }
+//        }
+
+        // 订阅 zlm启动事件, 新的zlm也会从这里进入系统
+        hookSubscribe.addSubscribe(ZLMHttpHookSubscribe.HookType.on_server_started,null,
+                (MediaServerItem mediaServerItem, JSONObject response)->{
+            ZLMServerConfig zlmServerConfig = JSONObject.toJavaObject(response, ZLMServerConfig.class);
+            if (zlmServerConfig !=null ) {
+                startGetMedia.remove(zlmServerConfig.getGeneralMediaServerId());
+                mediaServerService.handLeZLMServerConfig(zlmServerConfig);
+//                zLmRunning(zlmServerConfig);
+            }
+        });
+
+        // 获取zlm信息
+        logger.info("等待默认zlm接入...");
+
+        // 获取所有的zlm, 并开启主动连接
+        List<MediaServerItem> all = mediaServerService.getAllFromDatabase();
+        if (all.size() == 0) {
+            all.add(mediaConfig.getMediaSerItem());
+        }
+        for (MediaServerItem mediaServerItem : all) {
+            if (startGetMedia == null) startGetMedia = new HashMap<>();
+            startGetMedia.put(mediaServerItem.getId(), true);
+            new Thread(() -> {
+                ZLMServerConfig zlmServerConfig = getMediaServerConfig(mediaServerItem);
+                if (zlmServerConfig != null) {
+                    zlmServerConfig.setIp(mediaServerItem.getIp());
+                    zlmServerConfig.setHttpPort(mediaServerItem.getHttpPort());
+                    startGetMedia.remove(mediaServerItem.getId());
+                    mediaServerService.handLeZLMServerConfig(zlmServerConfig);
+                }
+            }).start();
+        }
+        Timer timer = new Timer();
+        // 2分钟后未连接到则不再去主动连接, TODO 并对重启前使用此在zlm的通道发送bye
+        timer.schedule(new TimerTask() {
+            @Override
+            public void run() {
+            if (startGetMedia != null) {
+                Set<String> allZlmId = startGetMedia.keySet();
+                for (String id : allZlmId) {
+                    logger.error("[ {} ]]主动连接失败,不再主动连接", id);
+                }
+                startGetMedia = null;
+            }
+            //  TODO 清理数据库中与redis不匹配的zlm
+            }
+        }, 60 * 1000 * 2);
+    }
+
+    public ZLMServerConfig getMediaServerConfig(MediaServerItem mediaServerItem) {
+        if (startGetMedia == null) return null;
+        if ( startGetMedia.get(mediaServerItem.getId()) == null || !startGetMedia.get(mediaServerItem.getId())) return null;
+        JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem);
+        ZLMServerConfig ZLMServerConfig = null;
+        if (responseJSON != null) {
+            JSONArray data = responseJSON.getJSONArray("data");
+            if (data != null && data.size() > 0) {
+                ZLMServerConfig = JSON.parseObject(JSON.toJSONString(data.get(0)), ZLMServerConfig.class);
+                ZLMServerConfig.setIp(mediaServerItem.getIp());
+            }
+        } else {
+            logger.error("[ {} ]-[ {}:{} ]主动连接失败失败, 2s后重试",
+                    mediaServerItem.getId(), mediaServerItem.getIp(), mediaServerItem.getHttpPort());
+            try {
+                Thread.sleep(2000);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+            ZLMServerConfig = getMediaServerConfig(mediaServerItem);
+        }
+        return ZLMServerConfig;
+
+    }
+
+    /**
+     * zlm 连接成功或者zlm重启后
+     */
+//    private void zLmRunning(ZLMServerConfig zlmServerConfig){
+//        logger.info( "[ id: " + zlmServerConfig.getGeneralMediaServerId() + "] zlm接入成功...");
+//        // 关闭循环获取zlm配置
+//        startGetMedia = false;
+//        MediaServerItem mediaServerItem = new MediaServerItem(zlmServerConfig, sipIp);
+//        storager.updateMediaServer(mediaServerItem);
+//
+//        if (mediaServerItem.isAutoConfig()) setZLMConfig(mediaServerItem);
+//        zlmServerManger.updateServerCatchFromHook(zlmServerConfig);
+//
+//        // 清空所有session
+////        zlmMediaListManager.clearAllSessions();
+//
+//        // 更新流列表
+//        zlmMediaListManager.updateMediaList(mediaServerItem);
+//        // 恢复流代理, 只查找这个这个流媒体
+//        List<StreamProxyItem> streamProxyListForEnable = storager.getStreamProxyListForEnableInMediaServer(
+//                mediaServerItem.getId(), true);
+//        for (StreamProxyItem streamProxyDto : streamProxyListForEnable) {
+//            logger.info("恢复流代理," + streamProxyDto.getApp() + "/" + streamProxyDto.getStream());
+//            JSONObject jsonObject = streamProxyService.addStreamProxyToZlm(streamProxyDto);
+//            if (jsonObject == null) {
+//                // 设置为未启用
+//                logger.info("恢复流代理失败,请检查流地址后重新启用" + streamProxyDto.getApp() + "/" + streamProxyDto.getStream());
+//                streamProxyService.stop(streamProxyDto.getApp(), streamProxyDto.getStream());
+//            }
+//        }
+//    }
+}

+ 794 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/ZLMServerConfig.java

@@ -0,0 +1,794 @@
+package com.zd.alg.iot.vmp.media.zlm;
+
+import com.alibaba.fastjson.annotation.JSONField;
+
+public class ZLMServerConfig {
+
+    @JSONField(name = "api.apiDebug")
+    private String apiDebug;
+
+    @JSONField(name = "api.secret")
+    private String apiSecret;
+
+    @JSONField(name = "ffmpeg.bin")
+    private String ffmpegBin;
+
+    @JSONField(name = "ffmpeg.cmd")
+    private String ffmpegCmd;
+
+    @JSONField(name = "ffmpeg.log")
+    private String ffmpegLog;
+
+    @JSONField(name = "general.enableVhost")
+    private String generalEnableVhost;
+
+    @JSONField(name = "general.mediaServerId")
+    private String generalMediaServerId;
+
+    @JSONField(name = "general.flowThreshold")
+    private String generalFlowThreshold;
+
+    @JSONField(name = "general.maxStreamWaitMS")
+    private String generalMaxStreamWaitMS;
+
+    @JSONField(name = "general.streamNoneReaderDelayMS")
+    private String generalStreamNoneReaderDelayMS;
+
+    @JSONField(name = "ip")
+    private String ip;
+
+    private String sdpIp;
+
+    private String streamIp;
+
+    private String hookIp;
+
+    private String updateTime;
+
+    private String createTime;
+
+    @JSONField(name = "hls.fileBufSize")
+    private String hlsFileBufSize;
+
+    @JSONField(name = "hls.filePath")
+    private String hlsFilePath;
+
+    @JSONField(name = "hls.segDur")
+    private String hlsSegDur;
+
+    @JSONField(name = "hls.segNum")
+    private String hlsSegNum;
+
+    @JSONField(name = "hook.access_file_except_hls")
+    private String hookAccessFileExceptHLS;
+
+    @JSONField(name = "hook.admin_params")
+    private String hookAdminParams;
+
+    @JSONField(name = "hook.enable")
+    private String hookEnable;
+
+    @JSONField(name = "hook.on_flow_report")
+    private String hookOnFlowReport;
+
+    @JSONField(name = "hook.on_http_access")
+    private String hookOnHttpAccess;
+
+    @JSONField(name = "hook.on_play")
+    private String hookOnPlay;
+
+    @JSONField(name = "hook.on_publish")
+    private String hookOnPublish;
+
+    @JSONField(name = "hook.on_record_mp4")
+    private String hookOnRecordMp4;
+
+    @JSONField(name = "hook.on_rtsp_auth")
+    private String hookOnRtspAuth;
+
+    @JSONField(name = "hook.on_rtsp_realm")
+    private String hookOnRtspRealm;
+
+    @JSONField(name = "hook.on_shell_login")
+    private String hookOnShellLogin;
+
+    @JSONField(name = "hook.on_stream_changed")
+    private String hookOnStreamChanged;
+
+    @JSONField(name = "hook.on_stream_none_reader")
+    private String hookOnStreamNoneReader;
+
+    @JSONField(name = "hook.on_stream_not_found")
+    private String hookOnStreamNotFound;
+
+    @JSONField(name = "hook.timeoutSec")
+    private String hookTimeoutSec;
+
+    @JSONField(name = "http.charSet")
+    private String httpCharSet;
+
+    @JSONField(name = "http.keepAliveSecond")
+    private String httpKeepAliveSecond;
+
+    @JSONField(name = "http.maxReqCount")
+    private String httpMaxReqCount;
+
+    @JSONField(name = "http.maxReqSize")
+    private String httpMaxReqSize;
+
+    @JSONField(name = "http.notFound")
+    private String httpNotFound;
+
+    @JSONField(name = "http.port")
+    private int httpPort;
+
+    @JSONField(name = "http.rootPath")
+    private String httpRootPath;
+
+    @JSONField(name = "http.sendBufSize")
+    private String httpSendBufSize;
+
+    @JSONField(name = "http.sslport")
+    private int httpSSLport;
+
+    @JSONField(name = "multicast.addrMax")
+    private String multicastAddrMax;
+
+    @JSONField(name = "multicast.addrMin")
+    private String multicastAddrMin;
+
+    @JSONField(name = "multicast.udpTTL")
+    private String multicastUdpTTL;
+
+    @JSONField(name = "record.appName")
+    private String recordAppName;
+
+    @JSONField(name = "record.filePath")
+    private String recordFilePath;
+
+    @JSONField(name = "record.fileSecond")
+    private String recordFileSecond;
+
+    @JSONField(name = "record.sampleMS")
+    private String recordFileSampleMS;
+
+    @JSONField(name = "rtmp.handshakeSecond")
+    private String rtmpHandshakeSecond;
+
+    @JSONField(name = "rtmp.keepAliveSecond")
+    private String rtmpKeepAliveSecond;
+
+    @JSONField(name = "rtmp.modifyStamp")
+    private String rtmpModifyStamp;
+
+    @JSONField(name = "rtmp.port")
+    private int rtmpPort;
+
+    @JSONField(name = "rtmp.sslport")
+    private int rtmpSslPort;
+
+    @JSONField(name = "rtp.audioMtuSize")
+    private String rtpAudioMtuSize;
+
+    @JSONField(name = "rtp.clearCount")
+    private String rtpClearCount;
+
+    @JSONField(name = "rtp.cycleMS")
+    private String rtpCycleMS;
+
+    @JSONField(name = "rtp.maxRtpCount")
+    private String rtpMaxRtpCount;
+
+    @JSONField(name = "rtp.videoMtuSize")
+    private String rtpVideoMtuSize;
+
+    @JSONField(name = "rtp_proxy.checkSource")
+    private String rtpProxyCheckSource;
+
+    @JSONField(name = "rtp_proxy.dumpDir")
+    private String rtpProxyDumpDir;
+
+    @JSONField(name = "rtp_proxy.port")
+    private int rtpProxyPort;
+
+    @JSONField(name = "rtp_proxy.timeoutSec")
+    private String rtpProxyTimeoutSec;
+
+    @JSONField(name = "rtsp.authBasic")
+    private String rtspAuthBasic;
+
+    @JSONField(name = "rtsp.handshakeSecond")
+    private String rtspHandshakeSecond;
+
+    @JSONField(name = "rtsp.keepAliveSecond")
+    private String rtspKeepAliveSecond;
+
+    @JSONField(name = "rtsp.port")
+    private int rtspPort;
+
+    @JSONField(name = "rtsp.sslport")
+    private int rtspSSlport;
+
+    @JSONField(name = "shell.maxReqSize")
+    private String shellMaxReqSize;
+
+    @JSONField(name = "shell.shell")
+    private String shellPhell;
+
+
+    public String getHookIp() {
+        return hookIp;
+    }
+
+    public void setHookIp(String hookIp) {
+        this.hookIp = hookIp;
+    }
+
+    public String getApiDebug() {
+        return apiDebug;
+    }
+
+    public void setApiDebug(String apiDebug) {
+        this.apiDebug = apiDebug;
+    }
+
+    public String getApiSecret() {
+        return apiSecret;
+    }
+
+    public void setApiSecret(String apiSecret) {
+        this.apiSecret = apiSecret;
+    }
+
+    public String getFfmpegBin() {
+        return ffmpegBin;
+    }
+
+    public void setFfmpegBin(String ffmpegBin) {
+        this.ffmpegBin = ffmpegBin;
+    }
+
+    public String getFfmpegCmd() {
+        return ffmpegCmd;
+    }
+
+    public void setFfmpegCmd(String ffmpegCmd) {
+        this.ffmpegCmd = ffmpegCmd;
+    }
+
+    public String getFfmpegLog() {
+        return ffmpegLog;
+    }
+
+    public void setFfmpegLog(String ffmpegLog) {
+        this.ffmpegLog = ffmpegLog;
+    }
+
+    public String getGeneralEnableVhost() {
+        return generalEnableVhost;
+    }
+
+    public void setGeneralEnableVhost(String generalEnableVhost) {
+        this.generalEnableVhost = generalEnableVhost;
+    }
+
+    public String getGeneralMediaServerId() {
+        return generalMediaServerId;
+    }
+
+    public void setGeneralMediaServerId(String generalMediaServerId) {
+        this.generalMediaServerId = generalMediaServerId;
+    }
+
+    public String getGeneralFlowThreshold() {
+        return generalFlowThreshold;
+    }
+
+    public void setGeneralFlowThreshold(String generalFlowThreshold) {
+        this.generalFlowThreshold = generalFlowThreshold;
+    }
+
+    public String getGeneralMaxStreamWaitMS() {
+        return generalMaxStreamWaitMS;
+    }
+
+    public void setGeneralMaxStreamWaitMS(String generalMaxStreamWaitMS) {
+        this.generalMaxStreamWaitMS = generalMaxStreamWaitMS;
+    }
+
+    public String getGeneralStreamNoneReaderDelayMS() {
+        return generalStreamNoneReaderDelayMS;
+    }
+
+    public void setGeneralStreamNoneReaderDelayMS(String generalStreamNoneReaderDelayMS) {
+        this.generalStreamNoneReaderDelayMS = generalStreamNoneReaderDelayMS;
+    }
+
+    public String getIp() {
+        return ip;
+    }
+
+    public void setIp(String ip) {
+        this.ip = ip;
+    }
+
+    public String getSdpIp() {
+        return sdpIp;
+    }
+
+    public void setSdpIp(String sdpIp) {
+        this.sdpIp = sdpIp;
+    }
+
+    public String getStreamIp() {
+        return streamIp;
+    }
+
+    public void setStreamIp(String streamIp) {
+        this.streamIp = streamIp;
+    }
+
+    public String getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(String updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public String getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(String createTime) {
+        this.createTime = createTime;
+    }
+
+    public String getHlsFileBufSize() {
+        return hlsFileBufSize;
+    }
+
+    public void setHlsFileBufSize(String hlsFileBufSize) {
+        this.hlsFileBufSize = hlsFileBufSize;
+    }
+
+    public String getHlsFilePath() {
+        return hlsFilePath;
+    }
+
+    public void setHlsFilePath(String hlsFilePath) {
+        this.hlsFilePath = hlsFilePath;
+    }
+
+    public String getHlsSegDur() {
+        return hlsSegDur;
+    }
+
+    public void setHlsSegDur(String hlsSegDur) {
+        this.hlsSegDur = hlsSegDur;
+    }
+
+    public String getHlsSegNum() {
+        return hlsSegNum;
+    }
+
+    public void setHlsSegNum(String hlsSegNum) {
+        this.hlsSegNum = hlsSegNum;
+    }
+
+    public String getHookAccessFileExceptHLS() {
+        return hookAccessFileExceptHLS;
+    }
+
+    public void setHookAccessFileExceptHLS(String hookAccessFileExceptHLS) {
+        this.hookAccessFileExceptHLS = hookAccessFileExceptHLS;
+    }
+
+    public String getHookAdminParams() {
+        return hookAdminParams;
+    }
+
+    public void setHookAdminParams(String hookAdminParams) {
+        this.hookAdminParams = hookAdminParams;
+    }
+
+    public String getHookEnable() {
+        return hookEnable;
+    }
+
+    public void setHookEnable(String hookEnable) {
+        this.hookEnable = hookEnable;
+    }
+
+    public String getHookOnFlowReport() {
+        return hookOnFlowReport;
+    }
+
+    public void setHookOnFlowReport(String hookOnFlowReport) {
+        this.hookOnFlowReport = hookOnFlowReport;
+    }
+
+    public String getHookOnHttpAccess() {
+        return hookOnHttpAccess;
+    }
+
+    public void setHookOnHttpAccess(String hookOnHttpAccess) {
+        this.hookOnHttpAccess = hookOnHttpAccess;
+    }
+
+    public String getHookOnPlay() {
+        return hookOnPlay;
+    }
+
+    public void setHookOnPlay(String hookOnPlay) {
+        this.hookOnPlay = hookOnPlay;
+    }
+
+    public String getHookOnPublish() {
+        return hookOnPublish;
+    }
+
+    public void setHookOnPublish(String hookOnPublish) {
+        this.hookOnPublish = hookOnPublish;
+    }
+
+    public String getHookOnRecordMp4() {
+        return hookOnRecordMp4;
+    }
+
+    public void setHookOnRecordMp4(String hookOnRecordMp4) {
+        this.hookOnRecordMp4 = hookOnRecordMp4;
+    }
+
+    public String getHookOnRtspAuth() {
+        return hookOnRtspAuth;
+    }
+
+    public void setHookOnRtspAuth(String hookOnRtspAuth) {
+        this.hookOnRtspAuth = hookOnRtspAuth;
+    }
+
+    public String getHookOnRtspRealm() {
+        return hookOnRtspRealm;
+    }
+
+    public void setHookOnRtspRealm(String hookOnRtspRealm) {
+        this.hookOnRtspRealm = hookOnRtspRealm;
+    }
+
+    public String getHookOnShellLogin() {
+        return hookOnShellLogin;
+    }
+
+    public void setHookOnShellLogin(String hookOnShellLogin) {
+        this.hookOnShellLogin = hookOnShellLogin;
+    }
+
+    public String getHookOnStreamChanged() {
+        return hookOnStreamChanged;
+    }
+
+    public void setHookOnStreamChanged(String hookOnStreamChanged) {
+        this.hookOnStreamChanged = hookOnStreamChanged;
+    }
+
+    public String getHookOnStreamNoneReader() {
+        return hookOnStreamNoneReader;
+    }
+
+    public void setHookOnStreamNoneReader(String hookOnStreamNoneReader) {
+        this.hookOnStreamNoneReader = hookOnStreamNoneReader;
+    }
+
+    public String getHookOnStreamNotFound() {
+        return hookOnStreamNotFound;
+    }
+
+    public void setHookOnStreamNotFound(String hookOnStreamNotFound) {
+        this.hookOnStreamNotFound = hookOnStreamNotFound;
+    }
+
+    public String getHookTimeoutSec() {
+        return hookTimeoutSec;
+    }
+
+    public void setHookTimeoutSec(String hookTimeoutSec) {
+        this.hookTimeoutSec = hookTimeoutSec;
+    }
+
+    public String getHttpCharSet() {
+        return httpCharSet;
+    }
+
+    public void setHttpCharSet(String httpCharSet) {
+        this.httpCharSet = httpCharSet;
+    }
+
+    public String getHttpKeepAliveSecond() {
+        return httpKeepAliveSecond;
+    }
+
+    public void setHttpKeepAliveSecond(String httpKeepAliveSecond) {
+        this.httpKeepAliveSecond = httpKeepAliveSecond;
+    }
+
+    public String getHttpMaxReqCount() {
+        return httpMaxReqCount;
+    }
+
+    public void setHttpMaxReqCount(String httpMaxReqCount) {
+        this.httpMaxReqCount = httpMaxReqCount;
+    }
+
+    public String getHttpMaxReqSize() {
+        return httpMaxReqSize;
+    }
+
+    public void setHttpMaxReqSize(String httpMaxReqSize) {
+        this.httpMaxReqSize = httpMaxReqSize;
+    }
+
+    public String getHttpNotFound() {
+        return httpNotFound;
+    }
+
+    public void setHttpNotFound(String httpNotFound) {
+        this.httpNotFound = httpNotFound;
+    }
+
+    public int getHttpPort() {
+        return httpPort;
+    }
+
+    public void setHttpPort(int httpPort) {
+        this.httpPort = httpPort;
+    }
+
+    public String getHttpRootPath() {
+        return httpRootPath;
+    }
+
+    public void setHttpRootPath(String httpRootPath) {
+        this.httpRootPath = httpRootPath;
+    }
+
+    public String getHttpSendBufSize() {
+        return httpSendBufSize;
+    }
+
+    public void setHttpSendBufSize(String httpSendBufSize) {
+        this.httpSendBufSize = httpSendBufSize;
+    }
+
+    public int getHttpSSLport() {
+        return httpSSLport;
+    }
+
+    public void setHttpSSLport(int httpSSLport) {
+        this.httpSSLport = httpSSLport;
+    }
+
+    public String getMulticastAddrMax() {
+        return multicastAddrMax;
+    }
+
+    public void setMulticastAddrMax(String multicastAddrMax) {
+        this.multicastAddrMax = multicastAddrMax;
+    }
+
+    public String getMulticastAddrMin() {
+        return multicastAddrMin;
+    }
+
+    public void setMulticastAddrMin(String multicastAddrMin) {
+        this.multicastAddrMin = multicastAddrMin;
+    }
+
+    public String getMulticastUdpTTL() {
+        return multicastUdpTTL;
+    }
+
+    public void setMulticastUdpTTL(String multicastUdpTTL) {
+        this.multicastUdpTTL = multicastUdpTTL;
+    }
+
+    public String getRecordAppName() {
+        return recordAppName;
+    }
+
+    public void setRecordAppName(String recordAppName) {
+        this.recordAppName = recordAppName;
+    }
+
+    public String getRecordFilePath() {
+        return recordFilePath;
+    }
+
+    public void setRecordFilePath(String recordFilePath) {
+        this.recordFilePath = recordFilePath;
+    }
+
+    public String getRecordFileSecond() {
+        return recordFileSecond;
+    }
+
+    public void setRecordFileSecond(String recordFileSecond) {
+        this.recordFileSecond = recordFileSecond;
+    }
+
+    public String getRecordFileSampleMS() {
+        return recordFileSampleMS;
+    }
+
+    public void setRecordFileSampleMS(String recordFileSampleMS) {
+        this.recordFileSampleMS = recordFileSampleMS;
+    }
+
+    public String getRtmpHandshakeSecond() {
+        return rtmpHandshakeSecond;
+    }
+
+    public void setRtmpHandshakeSecond(String rtmpHandshakeSecond) {
+        this.rtmpHandshakeSecond = rtmpHandshakeSecond;
+    }
+
+    public String getRtmpKeepAliveSecond() {
+        return rtmpKeepAliveSecond;
+    }
+
+    public void setRtmpKeepAliveSecond(String rtmpKeepAliveSecond) {
+        this.rtmpKeepAliveSecond = rtmpKeepAliveSecond;
+    }
+
+    public String getRtmpModifyStamp() {
+        return rtmpModifyStamp;
+    }
+
+    public void setRtmpModifyStamp(String rtmpModifyStamp) {
+        this.rtmpModifyStamp = rtmpModifyStamp;
+    }
+
+    public int getRtmpPort() {
+        return rtmpPort;
+    }
+
+    public void setRtmpPort(int rtmpPort) {
+        this.rtmpPort = rtmpPort;
+    }
+
+    public int getRtmpSslPort() {
+        return rtmpSslPort;
+    }
+
+    public void setRtmpSslPort(int rtmpSslPort) {
+        this.rtmpSslPort = rtmpSslPort;
+    }
+
+    public String getRtpAudioMtuSize() {
+        return rtpAudioMtuSize;
+    }
+
+    public void setRtpAudioMtuSize(String rtpAudioMtuSize) {
+        this.rtpAudioMtuSize = rtpAudioMtuSize;
+    }
+
+    public String getRtpClearCount() {
+        return rtpClearCount;
+    }
+
+    public void setRtpClearCount(String rtpClearCount) {
+        this.rtpClearCount = rtpClearCount;
+    }
+
+    public String getRtpCycleMS() {
+        return rtpCycleMS;
+    }
+
+    public void setRtpCycleMS(String rtpCycleMS) {
+        this.rtpCycleMS = rtpCycleMS;
+    }
+
+    public String getRtpMaxRtpCount() {
+        return rtpMaxRtpCount;
+    }
+
+    public void setRtpMaxRtpCount(String rtpMaxRtpCount) {
+        this.rtpMaxRtpCount = rtpMaxRtpCount;
+    }
+
+    public String getRtpVideoMtuSize() {
+        return rtpVideoMtuSize;
+    }
+
+    public void setRtpVideoMtuSize(String rtpVideoMtuSize) {
+        this.rtpVideoMtuSize = rtpVideoMtuSize;
+    }
+
+    public String getRtpProxyCheckSource() {
+        return rtpProxyCheckSource;
+    }
+
+    public void setRtpProxyCheckSource(String rtpProxyCheckSource) {
+        this.rtpProxyCheckSource = rtpProxyCheckSource;
+    }
+
+    public String getRtpProxyDumpDir() {
+        return rtpProxyDumpDir;
+    }
+
+    public void setRtpProxyDumpDir(String rtpProxyDumpDir) {
+        this.rtpProxyDumpDir = rtpProxyDumpDir;
+    }
+
+    public int getRtpProxyPort() {
+        return rtpProxyPort;
+    }
+
+    public void setRtpProxyPort(int rtpProxyPort) {
+        this.rtpProxyPort = rtpProxyPort;
+    }
+
+    public String getRtpProxyTimeoutSec() {
+        return rtpProxyTimeoutSec;
+    }
+
+    public void setRtpProxyTimeoutSec(String rtpProxyTimeoutSec) {
+        this.rtpProxyTimeoutSec = rtpProxyTimeoutSec;
+    }
+
+    public String getRtspAuthBasic() {
+        return rtspAuthBasic;
+    }
+
+    public void setRtspAuthBasic(String rtspAuthBasic) {
+        this.rtspAuthBasic = rtspAuthBasic;
+    }
+
+    public String getRtspHandshakeSecond() {
+        return rtspHandshakeSecond;
+    }
+
+    public void setRtspHandshakeSecond(String rtspHandshakeSecond) {
+        this.rtspHandshakeSecond = rtspHandshakeSecond;
+    }
+
+    public String getRtspKeepAliveSecond() {
+        return rtspKeepAliveSecond;
+    }
+
+    public void setRtspKeepAliveSecond(String rtspKeepAliveSecond) {
+        this.rtspKeepAliveSecond = rtspKeepAliveSecond;
+    }
+
+    public int getRtspPort() {
+        return rtspPort;
+    }
+
+    public void setRtspPort(int rtspPort) {
+        this.rtspPort = rtspPort;
+    }
+
+    public int getRtspSSlport() {
+        return rtspSSlport;
+    }
+
+    public void setRtspSSlport(int rtspSSlport) {
+        this.rtspSSlport = rtspSSlport;
+    }
+
+    public String getShellMaxReqSize() {
+        return shellMaxReqSize;
+    }
+
+    public void setShellMaxReqSize(String shellMaxReqSize) {
+        this.shellMaxReqSize = shellMaxReqSize;
+    }
+
+    public String getShellPhell() {
+        return shellPhell;
+    }
+
+    public void setShellPhell(String shellPhell) {
+        this.shellPhell = shellPhell;
+    }
+}

+ 379 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/dto/MediaItem.java

@@ -0,0 +1,379 @@
+package com.zd.alg.iot.vmp.media.zlm.dto;
+
+import java.util.List;
+
+public class MediaItem {
+
+    /**
+     * 应用名
+     */
+    private String app;
+
+    /**
+     * 流id
+     */
+    private String stream;
+
+    /**
+     * 观看总人数,包括hls/rtsp/rtmp/http-flv/ws-flv
+     */
+    private String totalReaderCount;
+
+    /**
+     * 协议 包括hls/rtsp/rtmp/http-flv/ws-flv
+     */
+    private String schema;
+
+
+    /**
+     * 产生源类型,
+     * unknown = 0,
+     * rtmp_push=1,
+     * rtsp_push=2,
+     * rtp_push=3,
+     * pull=4,
+     * ffmpeg_pull=5,
+     * mp4_vod=6,
+     * device_chn=7
+     */
+    private int originType;
+
+    /**
+     * 客户端和服务器网络信息,可能为null类型
+     */
+    private OriginSock originSock;
+
+    /**
+     * 产生源类型的字符串描述
+     */
+    private String originTypeStr;
+
+    /**
+     * 产生源的url
+     */
+    private String originUrl;
+
+    /**
+     * GMT unix系统时间戳,单位秒
+     */
+    private Long createStamp;
+
+    /**
+     * 存活时间,单位秒
+     */
+    private Long aliveSecond;
+
+    /**
+     * 数据产生速度,单位byte/s
+     */
+    private Long bytesSpeed;
+
+    /**
+     * 音视频轨道
+     */
+    private List<MediaTrack> tracks;
+
+    /**
+     * 音视频轨道
+     */
+    private String vhost;
+
+    /**
+     * 是否是docker部署, docker部署不会自动更新zlm使用的端口,需要自己手动修改
+     */
+    private boolean docker;
+
+    public static class MediaTrack {
+        /**
+         * 音频通道数
+         */
+        private int channels;
+
+        /**
+         *  H264 = 0, H265 = 1, AAC = 2, G711A = 3, G711U = 4
+         */
+        private int codecId;
+
+        /**
+         * 编码类型名称 CodecAAC CodecH264
+         */
+        private String codecIdName;
+
+        /**
+         * Video = 0, Audio = 1
+         */
+        private int codecType;
+
+        /**
+         * 轨道是否准备就绪
+         */
+        private boolean ready;
+
+        /**
+         * 音频采样位数
+         */
+        private int sampleBit;
+
+        /**
+         * 音频采样率
+         */
+        private int sampleRate;
+
+        /**
+         * 视频fps
+         */
+        private int fps;
+
+        /**
+         * 视频高
+         */
+        private int height;
+
+        /**
+         * 视频宽
+         */
+        private int width;
+
+        public int getChannels() {
+            return channels;
+        }
+
+        public void setChannels(int channels) {
+            this.channels = channels;
+        }
+
+        public int getCodecId() {
+            return codecId;
+        }
+
+        public void setCodecId(int codecId) {
+            this.codecId = codecId;
+        }
+
+        public String getCodecIdName() {
+            return codecIdName;
+        }
+
+        public void setCodecIdName(String codecIdName) {
+            this.codecIdName = codecIdName;
+        }
+
+        public int getCodecType() {
+            return codecType;
+        }
+
+        public void setCodecType(int codecType) {
+            this.codecType = codecType;
+        }
+
+        public boolean isReady() {
+            return ready;
+        }
+
+        public void setReady(boolean ready) {
+            this.ready = ready;
+        }
+
+        public int getSampleBit() {
+            return sampleBit;
+        }
+
+        public void setSampleBit(int sampleBit) {
+            this.sampleBit = sampleBit;
+        }
+
+        public int getSampleRate() {
+            return sampleRate;
+        }
+
+        public void setSampleRate(int sampleRate) {
+            this.sampleRate = sampleRate;
+        }
+
+        public int getFps() {
+            return fps;
+        }
+
+        public void setFps(int fps) {
+            this.fps = fps;
+        }
+
+        public int getHeight() {
+            return height;
+        }
+
+        public void setHeight(int height) {
+            this.height = height;
+        }
+
+        public int getWidth() {
+            return width;
+        }
+
+        public void setWidth(int width) {
+            this.width = width;
+        }
+    }
+
+    public static class OriginSock{
+        private String identifier;
+        private String local_ip;
+        private int local_port;
+        private String peer_ip;
+        private int peer_port;
+
+        public String getIdentifier() {
+            return identifier;
+        }
+
+        public void setIdentifier(String identifier) {
+            this.identifier = identifier;
+        }
+
+        public String getLocal_ip() {
+            return local_ip;
+        }
+
+        public void setLocal_ip(String local_ip) {
+            this.local_ip = local_ip;
+        }
+
+        public int getLocal_port() {
+            return local_port;
+        }
+
+        public void setLocal_port(int local_port) {
+            this.local_port = local_port;
+        }
+
+        public String getPeer_ip() {
+            return peer_ip;
+        }
+
+        public void setPeer_ip(String peer_ip) {
+            this.peer_ip = peer_ip;
+        }
+
+        public int getPeer_port() {
+            return peer_port;
+        }
+
+        public void setPeer_port(int peer_port) {
+            this.peer_port = peer_port;
+        }
+    }
+
+    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;
+    }
+
+    public String getTotalReaderCount() {
+        return totalReaderCount;
+    }
+
+    public void setTotalReaderCount(String totalReaderCount) {
+        this.totalReaderCount = totalReaderCount;
+    }
+
+
+    public int getOriginType() {
+        return originType;
+    }
+
+    public void setOriginType(int originType) {
+        this.originType = originType;
+    }
+
+
+    public String getOriginTypeStr() {
+        return originTypeStr;
+    }
+
+    public void setOriginTypeStr(String originTypeStr) {
+        this.originTypeStr = originTypeStr;
+    }
+
+    public String getOriginUrl() {
+        return originUrl;
+    }
+
+    public void setOriginUrl(String originUrl) {
+        this.originUrl = originUrl;
+    }
+
+    public Long getCreateStamp() {
+        return createStamp;
+    }
+
+    public void setCreateStamp(Long createStamp) {
+        this.createStamp = createStamp;
+    }
+
+    public Long getAliveSecond() {
+        return aliveSecond;
+    }
+
+    public void setAliveSecond(Long aliveSecond) {
+        this.aliveSecond = aliveSecond;
+    }
+
+    public List<MediaTrack> getTracks() {
+        return tracks;
+    }
+
+    public void setTracks(List<MediaTrack> tracks) {
+        this.tracks = tracks;
+    }
+
+    public String getSchema() {
+        return schema;
+    }
+
+    public void setSchema(String schema) {
+        this.schema = schema;
+    }
+
+    public void setOriginSock(OriginSock originSock) {
+        this.originSock = originSock;
+    }
+
+    public Long getBytesSpeed() {
+        return bytesSpeed;
+    }
+
+    public void setBytesSpeed(Long bytesSpeed) {
+        this.bytesSpeed = bytesSpeed;
+    }
+
+    public String getVhost() {
+        return vhost;
+    }
+
+    public void setVhost(String vhost) {
+        this.vhost = vhost;
+    }
+
+    public OriginSock getOriginSock() {
+        return originSock;
+    }
+
+    public boolean isDocker() {
+        return docker;
+    }
+
+    public void setDocker(boolean docker) {
+        this.docker = docker;
+    }
+}

+ 282 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/dto/MediaServerItem.java

@@ -0,0 +1,282 @@
+package com.zd.alg.iot.vmp.media.zlm.dto;
+
+
+import com.zd.alg.iot.vmp.gb28181.session.SsrcConfig;
+import com.zd.alg.iot.vmp.media.zlm.ZLMServerConfig;
+import org.springframework.util.StringUtils;
+
+import java.util.HashMap;
+
+public class MediaServerItem{
+
+    private String id;
+
+    private String ip;
+
+    private String hookIp;
+
+    private String sdpIp;
+
+    private String streamIp;
+
+    private int httpPort;
+
+    private int httpSSlPort;
+
+    private int rtmpPort;
+
+    private int rtmpSSlPort;
+
+    private int rtpProxyPort;
+
+    private int rtspPort;
+
+    private int rtspSSLPort;
+
+    private boolean autoConfig;
+
+    private String secret;
+
+    private String streamNoneReaderDelayMS;
+
+    private boolean rtpEnable;
+
+    private String rtpPortRange;
+
+    private int recordAssistPort;
+
+    private String createTime;
+
+    private String updateTime;
+
+    private boolean defaultServer;
+
+    private SsrcConfig ssrcConfig;
+
+    private int currentPort;
+
+
+    /**
+     * 每一台ZLM都有一套独立的SSRC列表
+     * 在ApplicationCheckRunner里对mediaServerSsrcMap进行初始化
+     */
+    private HashMap<String, SsrcConfig> mediaServerSsrcMap;
+
+    public MediaServerItem() {
+    }
+
+    public MediaServerItem(ZLMServerConfig zlmServerConfig, String sipIp) {
+        id = zlmServerConfig.getGeneralMediaServerId();
+        ip = zlmServerConfig.getIp();
+        hookIp = StringUtils.isEmpty(zlmServerConfig.getHookIp())? sipIp: zlmServerConfig.getHookIp();
+        sdpIp = StringUtils.isEmpty(zlmServerConfig.getSdpIp())? zlmServerConfig.getIp(): zlmServerConfig.getSdpIp();
+        streamIp = StringUtils.isEmpty(zlmServerConfig.getStreamIp())? zlmServerConfig.getIp(): zlmServerConfig.getStreamIp();
+        httpPort = zlmServerConfig.getHttpPort();
+        httpSSlPort = zlmServerConfig.getHttpSSLport();
+        rtmpPort = zlmServerConfig.getRtmpPort();
+        rtmpSSlPort = zlmServerConfig.getRtmpSslPort();
+        rtpProxyPort = zlmServerConfig.getRtpProxyPort();
+        rtspPort = zlmServerConfig.getRtspPort();
+        rtspSSLPort = zlmServerConfig.getRtspSSlport();
+        autoConfig = true; // 默认值true;
+        secret = zlmServerConfig.getApiSecret();
+        streamNoneReaderDelayMS = zlmServerConfig.getGeneralStreamNoneReaderDelayMS();
+        rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口
+        recordAssistPort = 0; // 默认关闭
+
+    }
+
+    public String getId() {
+        return id;
+    }
+
+    public void setId(String id) {
+        this.id = id;
+    }
+
+    public String getIp() {
+        return ip;
+    }
+
+    public void setIp(String ip) {
+        this.ip = ip;
+    }
+
+    public String getHookIp() {
+        return hookIp;
+    }
+
+    public void setHookIp(String hookIp) {
+        this.hookIp = hookIp;
+    }
+
+    public String getSdpIp() {
+        return sdpIp;
+    }
+
+    public void setSdpIp(String sdpIp) {
+        this.sdpIp = sdpIp;
+    }
+
+    public String getStreamIp() {
+        return streamIp;
+    }
+
+    public void setStreamIp(String streamIp) {
+        this.streamIp = streamIp;
+    }
+
+    public int getHttpPort() {
+        return httpPort;
+    }
+
+    public void setHttpPort(int httpPort) {
+        this.httpPort = httpPort;
+    }
+
+    public int getHttpSSlPort() {
+        return httpSSlPort;
+    }
+
+    public void setHttpSSlPort(int httpSSlPort) {
+        this.httpSSlPort = httpSSlPort;
+    }
+
+    public int getRtmpPort() {
+        return rtmpPort;
+    }
+
+    public void setRtmpPort(int rtmpPort) {
+        this.rtmpPort = rtmpPort;
+    }
+
+    public int getRtmpSSlPort() {
+        return rtmpSSlPort;
+    }
+
+    public void setRtmpSSlPort(int rtmpSSlPort) {
+        this.rtmpSSlPort = rtmpSSlPort;
+    }
+
+    public int getRtpProxyPort() {
+        return rtpProxyPort;
+    }
+
+    public void setRtpProxyPort(int rtpProxyPort) {
+        this.rtpProxyPort = rtpProxyPort;
+    }
+
+    public int getRtspPort() {
+        return rtspPort;
+    }
+
+    public void setRtspPort(int rtspPort) {
+        this.rtspPort = rtspPort;
+    }
+
+    public int getRtspSSLPort() {
+        return rtspSSLPort;
+    }
+
+    public void setRtspSSLPort(int rtspSSLPort) {
+        this.rtspSSLPort = rtspSSLPort;
+    }
+
+    public boolean isAutoConfig() {
+        return autoConfig;
+    }
+
+    public void setAutoConfig(boolean autoConfig) {
+        this.autoConfig = autoConfig;
+    }
+
+    public String getSecret() {
+        return secret;
+    }
+
+    public void setSecret(String secret) {
+        this.secret = secret;
+    }
+
+    public String getStreamNoneReaderDelayMS() {
+        return streamNoneReaderDelayMS;
+    }
+
+    public void setStreamNoneReaderDelayMS(String streamNoneReaderDelayMS) {
+        this.streamNoneReaderDelayMS = streamNoneReaderDelayMS;
+    }
+
+    public boolean isRtpEnable() {
+        return rtpEnable;
+    }
+
+    public void setRtpEnable(boolean rtpEnable) {
+        this.rtpEnable = rtpEnable;
+    }
+
+    public String getRtpPortRange() {
+        return rtpPortRange;
+    }
+
+    public void setRtpPortRange(String rtpPortRange) {
+        this.rtpPortRange = rtpPortRange;
+    }
+
+    public int getRecordAssistPort() {
+        return recordAssistPort;
+    }
+
+    public void setRecordAssistPort(int recordAssistPort) {
+        this.recordAssistPort = recordAssistPort;
+    }
+
+    public boolean isDefaultServer() {
+        return defaultServer;
+    }
+
+    public void setDefaultServer(boolean defaultServer) {
+        this.defaultServer = defaultServer;
+    }
+
+    public String getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(String createTime) {
+        this.createTime = createTime;
+    }
+
+    public String getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(String updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public HashMap<String, SsrcConfig> getMediaServerSsrcMap() {
+        return mediaServerSsrcMap;
+    }
+
+    public void setMediaServerSsrcMap(HashMap<String, SsrcConfig> mediaServerSsrcMap) {
+        this.mediaServerSsrcMap = mediaServerSsrcMap;
+    }
+
+    public SsrcConfig getSsrcConfig() {
+        return ssrcConfig;
+    }
+
+    public void setSsrcConfig(SsrcConfig ssrcConfig) {
+        this.ssrcConfig = ssrcConfig;
+    }
+
+    public int getCurrentPort() {
+        return currentPort;
+    }
+
+    public void setCurrentPort(int currentPort) {
+        this.currentPort = currentPort;
+    }
+
+
+}

+ 145 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/dto/StreamProxyItem.java

@@ -0,0 +1,145 @@
+package com.zd.alg.iot.vmp.media.zlm.dto;
+
+import com.zd.alg.iot.vmp.gb28181.bean.GbStream;
+
+public class StreamProxyItem extends GbStream {
+
+    private String type;
+    private String app;
+    private String stream;
+    private String mediaServerId;
+    private String url;
+    private String src_url;
+    private String dst_url;
+    private int timeout_ms;
+    private String ffmpeg_cmd_key;
+    private String rtp_type;
+    private boolean enable;
+    private boolean enable_hls;
+    private boolean enable_mp4;
+    private String platformGbId;
+    private String createTime;
+
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    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;
+    }
+
+    @Override
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    @Override
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getSrc_url() {
+        return src_url;
+    }
+
+    public void setSrc_url(String src_url) {
+        this.src_url = src_url;
+    }
+
+    public String getDst_url() {
+        return dst_url;
+    }
+
+    public void setDst_url(String dst_url) {
+        this.dst_url = dst_url;
+    }
+
+    public int getTimeout_ms() {
+        return timeout_ms;
+    }
+
+    public void setTimeout_ms(int timeout_ms) {
+        this.timeout_ms = timeout_ms;
+    }
+
+    public String getFfmpeg_cmd_key() {
+        return ffmpeg_cmd_key;
+    }
+
+    public void setFfmpeg_cmd_key(String ffmpeg_cmd_key) {
+        this.ffmpeg_cmd_key = ffmpeg_cmd_key;
+    }
+
+    public String getRtp_type() {
+        return rtp_type;
+    }
+
+    public void setRtp_type(String rtp_type) {
+        this.rtp_type = rtp_type;
+    }
+
+    public boolean isEnable() {
+        return enable;
+    }
+
+    public void setEnable(boolean enable) {
+        this.enable = enable;
+    }
+
+    public boolean isEnable_hls() {
+        return enable_hls;
+    }
+
+    public void setEnable_hls(boolean enable_hls) {
+        this.enable_hls = enable_hls;
+    }
+
+    public boolean isEnable_mp4() {
+        return enable_mp4;
+    }
+
+    public void setEnable_mp4(boolean enable_mp4) {
+        this.enable_mp4 = enable_mp4;
+    }
+
+
+    public String getPlatformGbId() {
+        return platformGbId;
+    }
+
+    public void setPlatformGbId(String platformGbId) {
+        this.platformGbId = platformGbId;
+    }
+
+    public String getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(String createTime) {
+        this.createTime = createTime;
+    }
+}

+ 220 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/dto/StreamPushItem.java

@@ -0,0 +1,220 @@
+package com.zd.alg.iot.vmp.media.zlm.dto;
+
+import com.zd.alg.iot.vmp.gb28181.bean.GbStream;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+
+public class StreamPushItem extends GbStream implements Comparable<StreamPushItem>{
+
+    /**
+     * 应用名
+     */
+    private String app;
+
+    /**
+     * 流id
+     */
+    private String stream;
+
+    /**
+     * 观看总人数,包括hls/rtsp/rtmp/http-flv/ws-flv
+     */
+    private String totalReaderCount;
+
+    /**
+     * 协议 包括hls/rtsp/rtmp/http-flv/ws-flv
+     */
+    private List<MediaSchema> schemas;
+
+    /**
+     * 产生源类型,
+     * unknown = 0,
+     * rtmp_push=1,
+     * rtsp_push=2,
+     * rtp_push=3,
+     * pull=4,
+     * ffmpeg_pull=5,
+     * mp4_vod=6,
+     * device_chn=7
+     */
+    private int originType;
+
+    /**
+     * 客户端和服务器网络信息,可能为null类型
+     */
+    private MediaItem.OriginSock originSock;
+
+    /**
+     * 产生源类型的字符串描述
+     */
+    private String originTypeStr;
+
+    /**
+     * 产生源的url
+     */
+    private String originUrl;
+
+    /**
+     * GMT unix系统时间戳,单位秒
+     */
+    private Long createStamp;
+
+    /**
+     * 存活时间,单位秒
+     */
+    private Long aliveSecond;
+
+    /**
+     * 音视频轨道
+     */
+    private List<MediaItem.MediaTrack> tracks;
+
+    /**
+     * 音视频轨道
+     */
+    private String vhost;
+
+    /**
+     * 使用的流媒体ID
+     */
+    private String mediaServerId;
+
+    public String getVhost() {
+        return vhost;
+    }
+
+    public void setVhost(String vhost) {
+        this.vhost = vhost;
+    }
+
+
+    @Override
+    public int compareTo(@NotNull StreamPushItem streamPushItem) {
+        return Long.valueOf(this.createStamp - streamPushItem.getCreateStamp().intValue()).intValue();
+    }
+
+    public static class MediaSchema {
+        private String schema;
+        private Long bytesSpeed;
+
+        public String getSchema() {
+            return schema;
+        }
+
+        public void setSchema(String schema) {
+            this.schema = schema;
+        }
+
+        public Long getBytesSpeed() {
+            return bytesSpeed;
+        }
+
+        public void setBytesSpeed(Long bytesSpeed) {
+            this.bytesSpeed = bytesSpeed;
+        }
+    }
+
+
+    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;
+    }
+
+    public String getTotalReaderCount() {
+        return totalReaderCount;
+    }
+
+    public void setTotalReaderCount(String totalReaderCount) {
+        this.totalReaderCount = totalReaderCount;
+    }
+
+    public List<MediaSchema> getSchemas() {
+        return schemas;
+    }
+
+    public void setSchemas(List<MediaSchema> schemas) {
+        this.schemas = schemas;
+    }
+
+    public int getOriginType() {
+        return originType;
+    }
+
+    public void setOriginType(int originType) {
+        this.originType = originType;
+    }
+
+    public MediaItem.OriginSock getOriginSock() {
+        return originSock;
+    }
+
+    public void setOriginSock(MediaItem.OriginSock originSock) {
+        this.originSock = originSock;
+    }
+
+
+    public String getOriginTypeStr() {
+        return originTypeStr;
+    }
+
+    public void setOriginTypeStr(String originTypeStr) {
+        this.originTypeStr = originTypeStr;
+    }
+
+    public String getOriginUrl() {
+        return originUrl;
+    }
+
+    public void setOriginUrl(String originUrl) {
+        this.originUrl = originUrl;
+    }
+
+    public Long getCreateStamp() {
+        return createStamp;
+    }
+
+    public void setCreateStamp(Long createStamp) {
+        this.createStamp = createStamp;
+    }
+
+    public Long getAliveSecond() {
+        return aliveSecond;
+    }
+
+    public void setAliveSecond(Long aliveSecond) {
+        this.aliveSecond = aliveSecond;
+    }
+
+    public List<MediaItem.MediaTrack> getTracks() {
+        return tracks;
+    }
+
+    public void setTracks(List<MediaItem.MediaTrack> tracks) {
+        this.tracks = tracks;
+    }
+
+
+    @Override
+    public String getMediaServerId() {
+        return mediaServerId;
+    }
+
+    @Override
+    public void setMediaServerId(String mediaServerId) {
+        this.mediaServerId = mediaServerId;
+    }
+}
+

+ 0 - 0
zd-modules/zd-algorithm/src/main/java/com/zd/alg/iot/vmp/media/zlm/dto/ZLMRunInfo.java


Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott